<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <id>https://web.dev/</id>
  <title>Pete LePage on web.dev</title>
  <updated>2026-04-15T23:21:06Z</updated>
  <author>
    <name>Pete LePage</name>
  </author>
  <link href="https://web.dev/authors/petelepage/feed.xml" rel="self"/>
  <link href="https://web.dev/"/>
  <icon>https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/3rFbsLsMMk1VveHfBRSu.jpg?auto=format</icon>
  <logo>https://web.dev/images/shared/rss-banner.png</logo>
  <subtitle>Developer Advocate on the web team at Google, taking the sharp edges off new stuff &amp;amp; helping make the web better for developers! (he/him)</subtitle>
  
  
  <entry>
    <title>How Chrome handles updates to the web app manifest</title>
    <link href="https://web.dev/manifest-updates/"/>
    <updated>2020-10-14T00:00:00Z</updated>
    <id>https://web.dev/manifest-updates/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; We are currently gathering data on browsers other than Chrome. If you would like to help us gather this data or add content to this page, please leave a comment in &lt;a href=&quot;https://github.com/GoogleChrome/web.dev/issues/4038&quot;&gt;issue #4038&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;When a PWA is installed, the browser uses information from the web app
manifest for the app name, the icons the app should use, and the URL that
should be opened when the app is launched. But what if you need to update
app shortcuts or try a new theme color? When and how are those changes
reflected in the browser?&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Do not change the name or location of your web app manifest file, doing so may prevent the browser from updating your PWA. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;In most cases, changes should be reflected within a day or two of the
PWA being launched, after the manifest has been updated.&lt;/p&gt;
&lt;h2 id=&quot;cr-desktop&quot;&gt;Updates on desktop Chrome &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-desktop&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the PWA is launched, or opened in a browser tab, Chrome determines the
last time the local manifest was checked for changes. If the manifest hasn&#39;t
been checked since the browser last started, or it hasn&#39;t been checked in the
last 24 hours, Chrome will make a network request for the manifest, then
compare it against the local copy.&lt;/p&gt;
&lt;p&gt;If select properties in the manifest have changed (see list below), Chrome
queues the new manifest, and after all windows have been closed, installs it.
Once installed, all fields from the new manifest (except &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;short_name&lt;/code&gt;,
and &lt;code&gt;icons&lt;/code&gt;) are updated.&lt;/p&gt;
&lt;h3 id=&quot;cr-desktop-trigger&quot;&gt;Which properties will trigger an update? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-desktop-trigger&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt; (see below)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shortcuts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start_url&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;theme_color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;file_handlers&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Changes to &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;short_name&lt;/code&gt; and &lt;code&gt;icons&lt;/code&gt; are &lt;strong&gt;not&lt;/strong&gt; currently supported on desktop Chrome, though work is underway to support them. &lt;/div&gt;&lt;/aside&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Changes to the &lt;code&gt;start_url&lt;/code&gt; require the manifest &lt;code&gt;id&lt;/code&gt; to be set. &lt;a href=&quot;https://developer.chrome.com/blog/pwa-manifest-id/&quot;&gt;More info&lt;/a&gt; &lt;/div&gt;&lt;/aside&gt;
&lt;!-- CrBug for name/shortname https://crbug.com/1088338 --&gt;
&lt;!-- CrBug for start_url https://crbug.com/1095947 --&gt;
&lt;h3 id=&quot;what-happens-when-the-display-field-is-updated&quot;&gt;What happens when the &lt;code&gt;display&lt;/code&gt; field is updated? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#what-happens-when-the-display-field-is-updated&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you update your app&#39;s display mode from &lt;code&gt;browser&lt;/code&gt; to &lt;code&gt;standalone&lt;/code&gt; your
existing users will not have their apps open in a window after updating. There
are two display settings for a web app, the one from the manifest (that you
control) and a window/browser tab setting controlled by the user. The user
preference is always respected.&lt;/p&gt;
&lt;h3 id=&quot;cr-desktop-test&quot;&gt;Testing manifest updates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-desktop-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;chrome://web-app-internals&lt;/code&gt; page (available in Chrome 85 or later),
includes detailed information about all of the PWAs installed on the device,
and can help you understand when the manifest was last updated, how often
it&#39;s updated, and more.&lt;/p&gt;
&lt;p&gt;To manually force Chrome to check for an updated manifest, you can either launch
Chrome with the &lt;a href=&quot;https://www.chromium.org/developers/how-tos/run-chromium-with-flags&quot; rel=&quot;noopener&quot;&gt;command line flag&lt;/a&gt; &lt;code&gt;--disable-manifest-update-throttle&lt;/code&gt;
or restart Chrome (use &lt;code&gt;about://restart&lt;/code&gt;), this resets the timer so that Chrome
will check for an updated manifest when the PWA is next launched. Then launch
the PWA. After closing the PWA, it should be updated with the new manifest
properties.&lt;/p&gt;
&lt;h3 id=&quot;cr-desktop-ref&quot;&gt;References &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-desktop-ref&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/1twU_yAoTDp4seZMmqrDzJFQtrM7Z60jXHkXjMIO2VpM/preview&quot; rel=&quot;noopener&quot;&gt;Updatable Web Manifest Fields&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;cr-android&quot;&gt;Updates on Chrome for Android &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-android&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the PWA is launched, Chrome determines the last time the local manifest
was checked for changes. If the manifest hasn&#39;t been checked in the last 24
hours, Chrome will schedule a network request for the manifest, then compare
it against the local copy.&lt;/p&gt;
&lt;p&gt;If select properties in the manifest have changed (see list below), Chrome
queues the new manifest, and after all windows of the PWA have been closed,
the device is plugged in, and connected to WiFi, Chrome requests an updated
WebAPK from the server. Once updated, all fields from the new manifest are
used.&lt;/p&gt;
&lt;h3 id=&quot;cr-android-trigger&quot;&gt;Which properties will trigger an update? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-android-trigger&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;background_color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orientation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shortcuts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start_url&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;theme_color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;web_share_target&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If Chrome is unable to get an updated manifest from the server, it may
increase the time between checks to 30 days.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Changes to &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;short_name&lt;/code&gt; and &lt;code&gt;icons&lt;/code&gt; are &lt;strong&gt;not&lt;/strong&gt; currently supported on Android Chrome, though work is underway to support them. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;cr-android-test&quot;&gt;Testing manifest updates &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-android-test&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;about://webapks&lt;/code&gt; page includes detailed information about all of the
PWAs installed on the device, and can tell you when the manifest was last
updated, how often it&#39;s updated, and more.&lt;/p&gt;
&lt;p&gt;To manually schedule an update to the manifest, overriding the timer and
local manifest do the following:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Plug in the device and ensure it&#39;s connected to WiFi.&lt;/li&gt;
&lt;li&gt;Use the Android task manager to shut down the PWA, then use the App panel
in Android settings to force stop the PWA.&lt;/li&gt;
&lt;li&gt;In Chrome, open &lt;code&gt;about://webapks&lt;/code&gt; and click the &amp;quot;Update&amp;quot; button for the
PWA. &amp;quot;Update Status&amp;quot; should change to &amp;quot;Pending&amp;quot;.&lt;/li&gt;
&lt;li&gt;Launch the PWA, and verify it&#39;s loaded properly.&lt;/li&gt;
&lt;li&gt;Use the Android task manager to shut down the PWA, then use the App panel
in Android settings to force stop the PWA.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The PWA usually updates within a few minutes, once the update has completed,
&amp;quot;Update Status&amp;quot; should change to &amp;quot;Successful&amp;quot;&lt;/p&gt;
&lt;h3 id=&quot;cr-android-ref&quot;&gt;References &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/manifest-updates/#cr-android-ref&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cs.chromium.org/chromium/src/chrome/browser/android/webapk/webapk.proto?l=35&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;UpdateReason&lt;/code&gt; enum&lt;/a&gt; for Chrome on Android&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author><author>
      <name>Adriana Jara</name>
    </author><author>
      <name>François Beaufort</name>
    </author>
  </entry>
  
  <entry>
    <title>Create an offline fallback page</title>
    <link href="https://web.dev/offline-fallback-page/"/>
    <updated>2020-09-24T00:00:00Z</updated>
    <id>https://web.dev/offline-fallback-page/</id>
    <content type="html" mode="escaped">&lt;p&gt;What do the Google Assistant app, the Slack app, the Zoom app, and almost
any other platform-specific app on your phone or computer have in common? Right, they always at least give you &lt;em&gt;something&lt;/em&gt;.
Even when you do not have a network connection, you can still open the Assistant app, or enter
Slack, or launch Zoom. You might not get anything particularly meaningful or even
be unable to achieve what you wanted to achieve, but at least you get &lt;em&gt;something&lt;/em&gt; and the app is in
control.&lt;/p&gt;
&lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-apps-wrapper&quot;&gt;
  &lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-assistant&quot; style=&quot;display: inline-block&quot;&gt;
    &lt;img alt=&quot;Google Assistant mobile app while offline.&quot; decoding=&quot;async&quot; height=&quot;1344&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 621px) 621px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gr49coayhLfP1UVJ2EeR.jpg?auto=format&amp;w=1242 1242w&quot; width=&quot;621&quot; /&gt;
    &lt;figcaption id=&quot;fig-assistant&quot;&gt;
      Google Assistant.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-slack&quot; style=&quot;display: inline-block&quot;&gt;
    &lt;img alt=&quot;Slack mobile app while offline.&quot; decoding=&quot;async&quot; height=&quot;1344&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 621px) 621px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/D4P00CQ15IE0plUEY3di.jpg?auto=format&amp;w=1242 1242w&quot; width=&quot;621&quot; /&gt;
    &lt;figcaption id=&quot;fig-slack&quot;&gt;
      Slack.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-zoom&quot; style=&quot;display: inline-block&quot;&gt;
    &lt;img alt=&quot;Zoom mobile app while offline.&quot; decoding=&quot;async&quot; height=&quot;1344&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 621px) 621px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/gw1LQG4JNYUDxQ2NOJHC.jpg?auto=format&amp;w=1242 1242w&quot; width=&quot;621&quot; /&gt;
    &lt;figcaption id=&quot;fig-zoom&quot;&gt;
      Zoom.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figcaption id=&quot;fig-apps-wrapper&quot;&gt;
    With platform-specific apps, even when you do not have a network connection, you never get nothing.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In contrast, on the Web, traditionally you get nothing when you are offline. Chrome gives you
the &lt;a href=&quot;https://www.blog.google/products/chrome/chrome-dino/&quot; rel=&quot;noopener&quot;&gt;offline dino game&lt;/a&gt;, but that is it.&lt;/p&gt;
&lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-offline-wrapper&quot;&gt;
  &lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-chrome-ios&quot; style=&quot;display: inline-block&quot;&gt;
    &lt;img alt=&quot;Google Chrome mobile app showing the offline dino game.&quot; decoding=&quot;async&quot; height=&quot;1731&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/yEf0wzIQ1hIf85xtUwse.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption id=&quot;fig-chrome-ios&quot;&gt;
      Google Chrome for iOS.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure role=&quot;group&quot; aria-labelledby=&quot;fig-chrome&quot; style=&quot;display: inline-block&quot;&gt;
    &lt;img alt=&quot;Google Chrome desktop app showing the offline dino game.&quot; decoding=&quot;async&quot; height=&quot;607&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vrqfLVP132LcydIWcYbh.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption id=&quot;fig-chrome&quot;&gt;
      Google Chrome for macOS.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figcaption id=&quot;fig-offline-wrapper&quot;&gt;
    On the Web, when you do not have a network connection, by default you get nothing.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;an-offline-fallback-page-with-a-custom-service-worker&quot;&gt;An offline fallback page with a custom service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#an-offline-fallback-page-with-a-custom-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It does not have to be this way, though. Thanks to
&lt;a href=&quot;https://web.dev/service-workers-cache-storage/&quot;&gt;service workers and the Cache Storage API&lt;/a&gt;, you can provide a
customized offline experience for your users. This can be a simple branded page with the information
that the user is currently offline, but it can just as well be a more creative solution, like, for
example, the famous &lt;a href=&quot;https://www.trivago.com/offline&quot; rel=&quot;noopener&quot;&gt;trivago offline maze game&lt;/a&gt; with a manual
&lt;strong&gt;Reconnect&lt;/strong&gt; button and an automatic reconnection attempt countdown.&lt;/p&gt;
&lt;figure&gt;
    &lt;img alt=&quot;The trivago offline page with the trivago offline maze.&quot; decoding=&quot;async&quot; height=&quot;616&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/0yvun9EV5758sRO9wSgY.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      The trivago offline maze.
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;h3 id=&quot;registering-the-service-worker&quot;&gt;Registering the service worker &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#registering-the-service-worker&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The way to make this happen is through a service worker. You can register a service worker
from your main page as in the code sample below. Usually you do this once
your app has loaded.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;load&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span 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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;serviceWorker&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; navigator&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;    navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;serviceWorker&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;register&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;service-worker.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;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;h3 id=&quot;the-service-worker-code&quot;&gt;The service worker code &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#the-service-worker-code&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The contents of the actual service worker file may seem a little involved at first sight, but the
comments in the sample below should clear things up. The core idea is to pre-cache a file named
&lt;code&gt;offline.html&lt;/code&gt; that only gets served on &lt;em&gt;failing&lt;/em&gt; navigation requests, and to let the browser handle
all other cases:&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 comment&quot;&gt;/*&lt;br /&gt;Copyright 2015, 2019, 2020, 2021 Google LLC. All Rights Reserved.&lt;br /&gt; Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&lt;br /&gt; you may not use this file except in compliance with the License.&lt;br /&gt; You may obtain a copy of the License at&lt;br /&gt; http://www.apache.org/licenses/LICENSE-2.0&lt;br /&gt; Unless required by applicable law or agreed to in writing, software&lt;br /&gt; distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&lt;br /&gt; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&lt;br /&gt; See the License for the specific language governing permissions and&lt;br /&gt; limitations under the License.&lt;br /&gt;*/&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Incrementing OFFLINE_VERSION will kick off the install event and force&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// previously cached resources to be updated from the network.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// This variable is intentionally declared and unused.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Add a comment for your linter if you want:&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// eslint-disable-next-line no-unused-vars&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;OFFLINE_VERSION&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;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;offline&quot;&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;// Customize this with a different URL if needed.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;OFFLINE_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;offline.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;install&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token 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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&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;waitUntil&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// Setting {cache: &#39;reload&#39;} in the new request ensures that the&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// response isn&#39;t fulfilled from the HTTP cache; i.e., it will be&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// from the network.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OFFLINE_URL&lt;/span&gt;&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;cache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;reload&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Force the waiting service worker to become the active service worker.&lt;/span&gt;&lt;br /&gt;  self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;skipWaiting&lt;/span&gt;&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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;activate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token 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 operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&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;waitUntil&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// Enable navigation preload if it&#39;s supported.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// See https://developers.google.com/web/updates/2017/02/navigation-preload&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 string&quot;&gt;&quot;navigationPreload&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&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;await&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;navigationPreload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enable&lt;/span&gt;&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;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 punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Tell the active service worker to take control of the page immediately.&lt;/span&gt;&lt;br /&gt;  self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;clients&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;claim&lt;/span&gt;&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 punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;self&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;fetch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token 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 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 comment&quot;&gt;// Only call event.respondWith() if this is a navigation request&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// for an HTML page.&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;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;mode &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;navigate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;respondWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token keyword&quot;&gt;try&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;// First, try to use the navigation preload response if it&#39;s&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;// supported.&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; preloadResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;preloadResponse&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;preloadResponse&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; preloadResponse&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 comment&quot;&gt;// Always try the network first.&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; networkResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&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;request&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; networkResponse&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;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;// catch is only triggered if an exception is thrown, which is&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;// likely due to a network error.&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;// If fetch() returns a valid HTTP response with a response code in&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token comment&quot;&gt;// the 4xx or 5xx range, the catch() will NOT be called.&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Fetch failed; returning offline page instead.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CACHE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;          &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cachedResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;OFFLINE_URL&lt;/span&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; cachedResponse&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;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 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 comment&quot;&gt;// If our if() condition is false, then this fetch handler won&#39;t&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// intercept the request. If there are any other fetch handlers&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// registered, they will get a chance to call event.respondWith().&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// If no fetch handlers call event.respondWith(), the request&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// will be handled by the browser as if there were no service&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// worker involvement.&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;h3 id=&quot;the-offline-fallback-page&quot;&gt;The offline fallback page &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#the-offline-fallback-page&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;offline.html&lt;/code&gt; file is where you can get creative and adapt it to your needs and add your
branding. The example below shows the bare minimum of what is possible.
It demonstrates both manual reload based on a button press as well as automatic reload
based on the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/online_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;online&lt;/code&gt; event&lt;/a&gt;
and regular server polling.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; You need to cache all resources required by your offline page. One way to deal with this is to inline everything, so the offline page is self-contained. This is what I do in the example below. &lt;/div&gt;&lt;/aside&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 doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&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;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;en&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;head&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&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;utf-8&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&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;X-UA-Compatible&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;IE=edge&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;width=device-width, initial-scale=1&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;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;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;You are offline&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;title&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;!-- Inline the page&#39;s stylesheet. --&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;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;body&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; helvetica&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arial&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;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2em&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 selector&quot;&gt;h1&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; italic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #373fff&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 selector&quot;&gt;p&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;margin-block&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class=&quot;token selector&quot;&gt;button&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;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&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;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;body&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;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;You are offline&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;h1&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;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Click the button below to try reloading.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&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;⤾ Reload&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;&amp;lt;!-- Inline the page&#39;s JavaScript file. --&gt;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// Manual reload feature.&lt;/span&gt;&lt;br /&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;&quot;button&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;click&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&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 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 comment&quot;&gt;// Listen to changes in the network state, reload when online.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// This handles the case when the device is completely offline.&lt;/span&gt;&lt;br /&gt;      window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;online&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&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 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 comment&quot;&gt;// Check if the server is responding and reload the page if it is.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// This handles the case when the device is online, but the server&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// is offline or misbehaving.&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;checkNetworkAndReload&lt;/span&gt;&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;try&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; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&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 comment&quot;&gt;// Verify we get a valid response from the server&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;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;status &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;500&lt;/span&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;            window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;location&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reload&lt;/span&gt;&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;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;span class=&quot;token keyword&quot;&gt;catch&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;// Unable to connect to the server, ignore.&lt;/span&gt;&lt;br /&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;        window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;checkNetworkAndReload&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2500&lt;/span&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;checkNetworkAndReload&lt;/span&gt;&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&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;html&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;demo&quot;&gt;Demo &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#demo&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can see the offline fallback page in action in the
&lt;a href=&quot;https://offline-fallback-demo.glitch.me/index.html&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt; embedded below. If you are
interested, you can explore the &lt;a href=&quot;https://glitch.com/edit/#!/offline-fallback-demo&quot; rel=&quot;noopener&quot;&gt;source code&lt;/a&gt; on
Glitch.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe src=&quot;https://glitch.com/embed/#!/embed/offline-fallback-demo?path=offline.html&amp;previewSize=100&quot; title=&quot;offline-fallback-demo on Glitch&quot; allow=&quot;geolocation; microphone; camera; midi; vr; encrypted-media&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot;&gt;
  &lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;side-note-on-making-your-app-installable&quot;&gt;Side note on making your app installable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#side-note-on-making-your-app-installable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now that your site has an offline fallback page, you might wonder about next steps. To make
your app installable, you need to add a &lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;web app manifest&lt;/a&gt; and optionally come up
with an &lt;a href=&quot;https://web.dev/define-install-strategy/&quot;&gt;install strategy&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;side-note-on-serving-an-offline-fallback-page-with-workboxjs&quot;&gt;Side note on serving an offline fallback page with Workbox.js &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/offline-fallback-page/#side-note-on-serving-an-offline-fallback-page-with-workboxjs&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You may have heard of &lt;a href=&quot;https://developer.chrome.com/docs/workbox/&quot; rel=&quot;noopener&quot;&gt;Workbox&lt;/a&gt;.
Workbox is a set of JavaScript libraries for adding offline support to web apps. If you prefer to
write less service worker code yourself, you can use the Workbox recipe for an
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/managing-fallback-responses/#offline-page-only&quot; rel=&quot;noopener&quot;&gt;offline page only&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Up next, learn &lt;a href=&quot;https://web.dev/define-install-strategy/&quot;&gt;how to define an install strategy&lt;/a&gt; for your app.&lt;/p&gt;
</content>
    <author>
      <name>Thomas Steiner</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>How to file a good browser bug</title>
    <link href="https://web.dev/how-to-file-a-good-bug/"/>
    <updated>2020-06-15T00:00:00Z</updated>
    <id>https://web.dev/how-to-file-a-good-bug/</id>
    <content type="html" mode="escaped">&lt;p&gt;Filing a good bug isn&#39;t hard, but takes a little work. The goal is to make it
easy to find what&#39;s broken, reach the root cause and, most importantly, find a
way to fix it. Bugs that make fast progress tend to be easy to reproduce with a
clear expected behavior.&lt;/p&gt;
&lt;h2 id=&quot;verify-it-is-a-bug&quot;&gt;Verify it is a bug &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#verify-it-is-a-bug&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step is to figure out what the &amp;quot;correct&amp;quot; behavior should be.&lt;/p&gt;
&lt;h3 id=&quot;whats-the-correct-behavior&quot;&gt;What&#39;s the correct behavior? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#whats-the-correct-behavior&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Check the relevant API docs on &lt;a href=&quot;https://developer.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;MDN&lt;/a&gt;, or try to
find related specs. This information can help you decide which API is actually
broken, where it&#39;s broken, and what the expected behavior is.&lt;/p&gt;
&lt;h3 id=&quot;does-it-work-in-a-different-browser&quot;&gt;Does it work in a different browser? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#does-it-work-in-a-different-browser&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Behavior that differs between browsers is generally prioritized higher as an
interoperability issue, especially when the browser containing the bug is the
odd one out. Try to test on the latest versions of Chrome, Firefox, Safari and
Edge, possibly using a tool like &lt;a href=&quot;https://www.browserstack.com/&quot; rel=&quot;noopener&quot;&gt;BrowserStack&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If possible, check that the page isn&#39;t intentionally behaving differently due to
user agent sniffing. In Chrome DevTools, try &lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/override-user-agent/&quot; rel=&quot;noopener&quot;&gt;setting the &lt;code&gt;User-Agent&lt;/code&gt; string
to another browser&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;did-it-break-in-a-recent-release&quot;&gt;Did it break in a recent release? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#did-it-break-in-a-recent-release&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Did this work as expected in the past, but broke in a recent browser release?
Such &amp;quot;regressions&amp;quot; can be acted upon much quicker, especially if you supply a
version number where it worked and a version where it failed. Tools like
&lt;a href=&quot;https://www.browserstack.com/&quot; rel=&quot;noopener&quot;&gt;BrowserStack&lt;/a&gt; can make it easy to check old
browser versions and the &lt;a href=&quot;https://www.chromium.org/developers/bisect-builds-py&quot; rel=&quot;noopener&quot;&gt;bisect-builds tool&lt;/a&gt;
(for Chromium) allows searching for the change very efficiently.&lt;/p&gt;
&lt;p&gt;If an issue is a regression and can be reproduced, the root cause can usually be
found and fixed quickly.&lt;/p&gt;
&lt;h3 id=&quot;are-others-seeing-the-same-problem&quot;&gt;Are others seeing the same problem? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#are-others-seeing-the-same-problem&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you&#39;re experiencing problems, there&#39;s a good chance other developers are too.
First, try searching for the bug on &lt;a href=&quot;http://stackoverflow.com/&quot; rel=&quot;noopener&quot;&gt;Stack Overflow&lt;/a&gt;.
This might help you translate an abstract problem into a specific broken API,
and it might help you find a short term workaround until the bug is fixed.&lt;/p&gt;
&lt;h2 id=&quot;has-it-been-reported-before&quot;&gt;Has it been reported before? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#has-it-been-reported-before&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have an idea of what the bug is, it&#39;s time to check to see if the bug
has already been reported by searching the browser bug database.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chromium-based browsers: &lt;a href=&quot;https://crbug.com/&quot; rel=&quot;noopener&quot;&gt;https://crbug.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firefox: &lt;a href=&quot;https://bugzilla.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;https://bugzilla.mozilla.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Safari &amp;amp; WebKit-based browsers: &lt;a href=&quot;https://bugs.webkit.org/&quot; rel=&quot;noopener&quot;&gt;https://bugs.webkit.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you find an existing bug that describes the problem, add your support
by starring, favoriting, or commenting on the bug. And, on many sites,
you can add yourself to the CC list and get updates when the bug changes.&lt;/p&gt;
&lt;p&gt;If you decide to comment on the bug, include information about how the bug
affects your website. Avoid adding &amp;quot;+1&amp;quot; style comments, as bug trackers
typically send emails for every comment.&lt;/p&gt;
&lt;h2 id=&quot;report-the-bug&quot;&gt;Report the bug &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#report-the-bug&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the bug hasn&#39;t been reported before, it&#39;s time to tell the browser vendor
about it.&lt;/p&gt;
&lt;h3 id=&quot;minified-test-case&quot;&gt;Create a minimized test case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#minified-test-case&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Mozilla has a great article on
&lt;a href=&quot;https://developer.mozilla.org/docs/Mozilla/QA/Reducing_testcases&quot; rel=&quot;noopener&quot;&gt;how to create a minimized test case&lt;/a&gt;. To make a
long story short, while a description of the problem is a great start, nothing
beats providing a linked demo in the bug that shows the
problem. To maximize the chance of fast progress the example should contain
the minimum possible code needed to demonstrate the problem. A minimal code
sample is the number one thing you can do to increase the odds of your
bug getting fixed.&lt;/p&gt;
&lt;p&gt;Here are a few tips for minimizing a test case:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Download the web page, add
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/base&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;base href=&amp;quot;https://original.url&amp;quot;&amp;gt;&lt;/code&gt;&lt;/a&gt;
and verify that the bug exists locally. This may require a live HTTPS server if the
URL uses HTTPS.&lt;/li&gt;
&lt;li&gt;Test the local files on the latest builds of as many browsers as you can.&lt;/li&gt;
&lt;li&gt;Try to condense everything into 1 file.&lt;/li&gt;
&lt;li&gt;Remove code (starting with things you know to be unnecessary) until the bug
goes away.&lt;/li&gt;
&lt;li&gt;Use version control so that you can save your work and undo things that go
wrong.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;hosting-a-minified-test-case&quot;&gt;Hosting a minified test case &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#hosting-a-minified-test-case&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If you&#39;re looking for a good place to host your minified test case,
there are several good places available:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://glitch.com/&quot; rel=&quot;noopener&quot;&gt;Glitch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jsbin.com/&quot; rel=&quot;noopener&quot;&gt;JSBin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://jsfiddle.net/&quot; rel=&quot;noopener&quot;&gt;JSFiddle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://codepen.io/&quot; rel=&quot;noopener&quot;&gt;CodePen&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be aware that several of those sites display content in an iframe, which
may cause features or bugs to behave differently.&lt;/p&gt;
&lt;h2 id=&quot;filing-your-issue&quot;&gt;Filing your issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#filing-your-issue&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve got your minimized test case, you&#39;re ready to file that bug.
Head over to the right bug tracking site, and create a new issue.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chromium-based browsers - &lt;a href=&quot;https://crbug.com/new&quot; rel=&quot;noopener&quot;&gt;https://crbug.com/new&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Firefox - &lt;a href=&quot;https://bugzilla.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;https://bugzilla.mozilla.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Safari &amp;amp; WebKit-based browsers - &lt;a href=&quot;https://bugs.webkit.org/&quot; rel=&quot;noopener&quot;&gt;https://bugs.webkit.org/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;provide-a-clear-description-and-the-steps-required-to-reproduce-the-issue&quot;&gt;Provide a clear description and the steps required to reproduce the issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#provide-a-clear-description-and-the-steps-required-to-reproduce-the-issue&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, provide a clear description to help engineers quickly understand what
the problem is and help to triage the issue.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;When installing a PWA using the `beforeinstallprompt.prompt()`, the&lt;br /&gt;`appinstalled` event fires before the call to `prompt()` resolves.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Next, provide the detailed steps required to reproduce the issue.
This is where your &lt;a href=&quot;https://web.dev/how-to-file-a-good-bug/#minified-test-case&quot;&gt;minified test case&lt;/a&gt; comes in.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;What steps will reproduce the problem?&lt;br /&gt;1. Go to https://basic-pwa.glitch.me/, open DevTools and look at the&lt;br /&gt;   console tab.&lt;br /&gt;2. Click the Install button in the page, you might need to interact with&lt;br /&gt;   the page a bit before it becomes enabled.&lt;br /&gt;3. Click Install on the browser modal install confirmation.&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And finally, describe the &lt;em&gt;expected&lt;/em&gt;, and &lt;em&gt;actual&lt;/em&gt; result.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;What is the expected result? In the console:&lt;br /&gt;0. INSTALL: Available (logged when `beforeinstallprompt` event fired)&lt;br /&gt;1. INSTALL_PROMPT_RESPONSE: {outcome: &quot;accepted&quot;, platform: &quot;web&quot;}&lt;br /&gt;   (logged when beforeinstallprompt.prompt()` resolves)&lt;br /&gt;2. INSTALL: Success (logged when `appinstalled` event fired)&lt;br /&gt;&lt;br /&gt;What is the actual result? In the console:&lt;br /&gt;0. INSTALL: Available (logged when `beforeinstallprompt` event fired)&lt;br /&gt;1. INSTALL: Success (logged when `appinstalled` event fired)&lt;br /&gt;2. INSTALL_PROMPT_RESPONSE: {outcome: &quot;accepted&quot;, platform: &quot;web&quot;}&lt;br /&gt;   (logged when beforeinstallprompt.prompt()` resolves)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;For more information, check out &lt;a href=&quot;https://developer.mozilla.org/docs/Mozilla/QA/Bug_writing_guidelines&quot; rel=&quot;noopener&quot;&gt;Bug report writing guidelines&lt;/a&gt;
on MDN.&lt;/p&gt;
&lt;h4 id=&quot;bonus-add-a-screenshot-or-screencast-of-the-issue&quot;&gt;Bonus: Add a screenshot or screencast of the issue &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#bonus-add-a-screenshot-or-screencast-of-the-issue&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Though not required, in some cases, it can be helpful to add a screenshot,
or screencast of the issue. This is especially helpful in cases where bugs
may require some odd steps to reproduce. Being able to see what happens in
a screencast, or on a screenshot can frequently be helpful.&lt;/p&gt;
&lt;h3 id=&quot;include-details-of-the-environment&quot;&gt;Include details of the environment &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#include-details-of-the-environment&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some bugs are reproducible only on certain operating systems, or only on
specific kinds of displays (for example, low-dpi or high-dpi). Be sure to
include the details of any test environments you used.&lt;/p&gt;
&lt;h3 id=&quot;submit-the-bug&quot;&gt;Submit the bug &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/how-to-file-a-good-bug/#submit-the-bug&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, submit the bug. Then, remember to keep an eye on your email for any
responses to the bug. Typically during investigation and when fixing the bug,
engineers may have additional questions, or if they have difficulty
reproducing the issue, they may reach out.&lt;/p&gt;
</content>
    <author>
      <name>Robert Nyman</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>How to define your install strategy</title>
    <link href="https://web.dev/define-install-strategy/"/>
    <updated>2020-05-12T00:00:00Z</updated>
    <id>https://web.dev/define-install-strategy/</id>
    <content type="html" mode="escaped">&lt;p&gt;In the past, app installation was only possible in the context of platform-specific applications. Today, modern web apps offer installable experiences that provide the same level of integration and reliability as platform-specific apps.&lt;/p&gt;
&lt;p&gt;You can achieve this in different ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installing the PWA &lt;a href=&quot;https://web.dev/customize-install/&quot;&gt;from the browser&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Installing the PWA &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;from the app store&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having different distribution channels is a powerful way of reaching a broad number of users, but choosing the right strategy to promote your the installation of your PWA can be challenging.&lt;/p&gt;
&lt;p&gt;This guide explores best practices for combining different installation options to increase installation rates and avoid platform competition and cannibalization. The installation offerings covered include PWAs installed from both the browser and the App Store, as well as platform-specific apps.&lt;/p&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;6R9pupbDXYw&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;h2 id=&quot;why-make-your-web-app-installable&quot;&gt;Why make your web app installable? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#why-make-your-web-app-installable&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Installed Progressive Web Apps run in a standalone window instead of a browser tab. They&#39;re launchable from the user&#39;s home screen, dock, taskbar, or shelf. It&#39;s possible to search for them on a device and jump between them with the app switcher, making them feel like part of the device they&#39;re installed on.&lt;/p&gt;
&lt;p&gt;But having both an installable web app and a platform-specific app can be confusing for users. For some users platform-specific apps may be the best choice, but for others they can present some drawbacks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Storage constraints:&lt;/strong&gt; installing a new app might mean deleting others, or cleaning up space, by removing valuable content. This is especially disadvantageous for users on low-end devices.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Available bandwidth:&lt;/strong&gt; downloading an app can be a costly and slow process, even more so for users on slow connections and expensive data plans.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Friction:&lt;/strong&gt; leaving a website and moving to a store to download an app creates additional friction and delays a user action that could be performed directly in the web.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Update cycle:&lt;/strong&gt; making changes in platform-specific apps might require going through an app review process, which can slow down changes and experiments (A/B tests, for example).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In some cases, the percentage of users who won&#39;t download your platform-specific app might be large, for example: those that think that they won&#39;t use the app very often, or can&#39;t justify spending several megabytes of storage or data. You can determine the size of this segment in several ways, for example by using an analytics setup to track the percentage of &amp;quot;mobile web only&amp;quot; users.&lt;/p&gt;
&lt;p&gt;If the size of this segment is considerable, that&#39;s a good indication that you need to provide alternative ways of installing your experiences.&lt;/p&gt;
&lt;h2 id=&quot;promoting-installation-of-your-pwa-through-the-browser&quot;&gt;Promoting installation of your PWA through the browser &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-installation-of-your-pwa-through-the-browser&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have a high quality PWA, it may be better to promote its installation over your platform-specific app. For example, if the platform-specific app is missing functionality offered by your PWA, or if it hasn&#39;t been updated in a while. It can also be helpful to promote installation of your PWA if the platform-specific app wasn&#39;t optimized for bigger screens, such as on ChromeOS.&lt;/p&gt;
&lt;p&gt;For some apps, driving platform-specific app installations is a key part of the business model, in that case, it makes business sense to promote installation of your platform-specific app. But, some users might be more comfortable staying on the web. If that segment can be identified, the PWA prompt can be shown only to them (what we call &amp;quot;PWA as fallback&amp;quot;).&lt;/p&gt;
&lt;h3 id=&quot;pwa-as-primary-installable-experience&quot;&gt;PWA as primary installable experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#pwa-as-primary-installable-experience&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once a PWA meets the &lt;a href=&quot;https://web.dev/install-criteria/&quot;&gt;installability criteria&lt;/a&gt;, most browsers show an indication that the PWA is installable. For example, desktop Chrome shows an installable icon in the address bar, and on mobile, it shows a mini-infobar:&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Standard Chrome install prompt, called a mini-infobar&quot; decoding=&quot;async&quot; height=&quot;208&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1VOvbQjeenZOBAmzjVN5.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    The mini-infobar.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;While that may be enough for some experiences, if your goal is to drive installations of your PWA, we highly recommend you listen for the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/BeforeInstallPromptEvent&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;BeforeInstallPromptEvent&lt;/code&gt;&lt;/a&gt;, and follow the &lt;a href=&quot;https://web.dev/promote-install/&quot;&gt;patterns for promoting the installation&lt;/a&gt; of your PWA.&lt;/p&gt;
&lt;h2 id=&quot;prevent-your-pwa-from-cannibalizing-your-platform-specific-app-install-rate&quot;&gt;Prevent your PWA from cannibalizing your platform-specific app install rate &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#prevent-your-pwa-from-cannibalizing-your-platform-specific-app-install-rate&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In some cases, you may choose to promote the installation of your platform-specific app over your PWA, but in this case, we still recommend you provide a mechanism to allow users to install your PWA. This fallback option makes it possible for users who can&#39;t, or don&#39;t want to install your platform-specific app to get a similar, installed experience.&lt;/p&gt;
&lt;p&gt;The first step to implement this strategy is to define a heuristic for when you&#39;ll show the user an install promotion for your PWA.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For example:&lt;/strong&gt; A PWA user is a user that has seen the platform-specific app install prompt and not installed the platform-specific app. They have returned to the site at least five times, or they have clicked the app banner, but have continued using the website instead.&lt;/p&gt;
&lt;p&gt;Then, the heuristic can be implemented in the following way:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Show the platform-specific app install banner.&lt;/li&gt;
&lt;li&gt;If a user dismisses the banner, set a cookie with that information (e.g. &lt;code&gt;document.cookie = &amp;quot;app-install-banner=dismissed&amp;quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use another cookie to track the number of user visits to the site (e.g. &lt;code&gt;document.cookie = &amp;quot;user-visits=1&amp;quot;&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Write a function, such as &lt;code&gt;isPWAUser()&lt;/code&gt;, that uses the information previously stored in the cookies along with the &lt;a href=&quot;https://web.dev/get-installed-related-apps/&quot;&gt;&lt;code&gt;getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; API to determine if a user is considered a &amp;quot;PWA user&amp;quot;.&lt;/li&gt;
&lt;li&gt;When the user performs a meaningful action, call &lt;code&gt;isPWAUser()&lt;/code&gt;. If the function returns true and the PWA install prompt was saved previously, you can show the PWA install button.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;promoting-installation-of-your-pwa-through-an-app-store&quot;&gt;Promoting installation of your PWA through an app store &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-installation-of-your-pwa-through-an-app-store&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Apps for App stores can be built with different technologies, including PWA techniques. In &lt;a href=&quot;https://youtu.be/V7YX4cZ_Cto&quot; rel=&quot;noopener&quot;&gt;Blending PWA into native environments&lt;/a&gt; you can find a summary of the technologies that can be used to that end.&lt;/p&gt;
&lt;p&gt;In this section we&#39;ll classify apps in the store in two groups:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Platform-specific apps:&lt;/strong&gt; these apps are mostly built with platform-specific code. Their sizes depend on the platform, but they&#39;re usually above 10MB on Android, and 30MB on iOS. You might want to promote your platform-specific app if you don&#39;t have a PWA, or if the platform-specific app presents a more complete feature set.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lightweight apps:&lt;/strong&gt; these apps can be built with platform-specific code as well, but they are commonly built with web technology, packaged in a platform-specific wrapper. Full PWAs can be uploaded to the stores as well. (This is discussed &lt;a href=&quot;https://web.dev/define-install-strategy/#promoting-lightweight-apps&quot;&gt;later&lt;/a&gt; in this article.) Some companies opt to provide these as &amp;quot;lite&amp;quot; experiences, and others have used this approach for their flagship (core) apps as well.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;promoting-lightweight-apps&quot;&gt;Promoting Lightweight Apps &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#promoting-lightweight-apps&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;According to a &lt;a href=&quot;https://medium.com/googleplaydev/shrinking-apks-growing-installs-5d3fcba23ce2&quot; rel=&quot;noopener&quot;&gt;Google Play study&lt;/a&gt;, for every 6 MB increase to an APK&#39;s size, the install conversion rate decreases by 1%. This means that the download completion rate of an app of 10 MB could be approximately &lt;strong&gt;30% higher than an app of 100 MB!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;To address this, some companies are leveraging their PWA to provide a lightweight version of their app in the Play Store using &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;Trusted Web Activities&lt;/a&gt; (TWA). TWAs wrap your PWA in a webview like component, and the resulting app size is usually only a few megabytes.&lt;/p&gt;
&lt;p&gt;Oyo, one of India&#39;s largest hospitality companies, built a &lt;a href=&quot;https://web.dev/oyo-lite-twa/&quot;&gt;Lite version of their app&lt;/a&gt;, and made it available in the Play Store using a TWA. At the time this article was written, the Oyo app was only 850 KB, just 7% the size of their Android app. And once installed, it&#39;s indistinguishable from their Android app:&lt;/p&gt;
&lt;figure&gt;
  &lt;video controls=&quot;&quot; autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/oyo-case-study/oyo-lite.webm&quot; type=&quot;video/webm; codecs=vp8&quot; /&gt;
    &lt;source src=&quot;https://storage.googleapis.com/web-dev-assets/oyo-case-study/oyo-lite.mp4&quot; type=&quot;video/mp4; codecs=h264&quot; /&gt;
  &lt;/video&gt;
 &lt;figcaption&gt;
    OYO Lite in action.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Oyo kept both the flagship and &amp;quot;lite&amp;quot; app versions in the store, providing a choice to their users.&lt;/p&gt;
&lt;h4 id=&quot;providing-a-lightweight-web-experience&quot;&gt;Providing a lightweight web experience &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#providing-a-lightweight-web-experience&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Intuitively, users on low-end devices, might be more inclined to download lightweight versions of apps than users on high-end phones. Therefore, if it&#39;s possible to identify a user&#39;s device, you could prioritize the lightweight app install banner over the heavier platform-specific app version.&lt;/p&gt;
&lt;p&gt;On the web, it&#39;s possible to obtain device signals and approximately map them to device categories (e.g. &amp;quot;high&amp;quot;, &amp;quot;mid&amp;quot;, or &amp;quot;low&amp;quot;). You can obtain this information in different ways, using either JavaScript APIs or client hints.&lt;/p&gt;
&lt;h4 id=&quot;using-javascript&quot;&gt;Using JavaScript &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#using-javascript&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Using JavaScript properties such as &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/NavigatorConcurrentHardware/hardwareConcurrency&quot; rel=&quot;noopener&quot;&gt;navigator.hardwareConcurrency&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/deviceMemory&quot; rel=&quot;noopener&quot;&gt;navigator.deviceMemory&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Navigator/connection&quot; rel=&quot;noopener&quot;&gt;navigator.connection&lt;/a&gt; you can get information about the device CPU, memory and network status respectively. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; deviceCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Memory&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lite&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;full&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;`&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h4 id=&quot;using-client-hints&quot;&gt;Using client hints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#using-client-hints&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Device signals can be also inferred in HTTP request headers, through &lt;a href=&quot;https://developer.mozilla.org/docs/Glossary/Client_hints&quot; rel=&quot;noopener&quot;&gt;client hints&lt;/a&gt;. Here&#39;s how you can implement the previous code for device memory with client hints:&lt;/p&gt;
&lt;p&gt;First, tell the browser you are interested in receiving device memory hints in the header of the HTTP response for any first-party request:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;HTTP/1.1 200 OK&lt;br /&gt;Content-Type: text/html&lt;br /&gt;Accept-CH: Device-Memory&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Then, you&#39;ll start receiving &lt;code&gt;Device-Memory&lt;/code&gt; information in the request header of HTTP requests:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;GET /main.js HTTP/1.1&lt;br /&gt;Device-Memory: 0.5&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can use this information in your backends to store a cookie with the category of the user&#39;s device:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/route&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&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 comment&quot;&gt;// Determine device category&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; deviceCategory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Memory&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;lite&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;full&#39;&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 comment&quot;&gt;// Set cookie&lt;/span&gt;&lt;br /&gt;  res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setCookie&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Device-Category&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deviceCategory&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 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;Finally, create your own logic to map this information to device categories, and show the corresponding app install prompt on each case:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&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 function&quot;&gt;isDeviceMidOrLowEnd&lt;/span&gt;&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 comment&quot;&gt;// show &quot;Lite app&quot; install banner or PWA A2HS prompt&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 comment&quot;&gt;// show &quot;Core app&quot; install banner&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Covering in depth techniques on how to map device signals to device categories is out of the scope of this guide, but you can check out Addy Osmani&#39;s &lt;a href=&quot;https://dev.to/addyosmani/adaptive-loading-improving-web-performance-on-low-end-devices-1m69&quot;&gt;adaptive loading guide&lt;/a&gt;, Philip Walton&#39;s &lt;a href=&quot;https://developer.chrome.com/blog/device-memory/&quot;&gt;The Device Memory API&lt;/a&gt;  and Jeremy Wagner&#39;s &lt;a href=&quot;https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/client-hints/&quot;&gt;Adapting to Users with Client Hints&lt;/a&gt; for more information on best practices around this. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/define-install-strategy/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The ability to have an icon in the user&#39;s home screen is one of the most engaging features of applications. Given that historically this was only possible for apps installed from app stores, companies might think that showing an app store install banner would be enough to convince users to install their experiences. Currently there are more options for letting users install an app, including offering lightweight app experiences in the stores, and letting users add PWAs to the home screen by prompting them to do it directly from the website.&lt;/p&gt;
</content>
    <author>
      <name>Demian Renzulli</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Persistent storage</title>
    <link href="https://web.dev/persistent-storage/"/>
    <updated>2020-05-12T00:00:00Z</updated>
    <id>https://web.dev/persistent-storage/</id>
    <content type="html" mode="escaped">&lt;p&gt;When faced with storage pressure like low disk space, browsers will
typically &lt;a href=&quot;https://web.dev/storage-for-the-web/#eviction&quot;&gt;evict data&lt;/a&gt;, including from the Cache API and IndexedDB,
from the least recently used origin. This may cause data loss if the app
hasn&#39;t synced data with the server, and reduce the reliability of the app by
removing resources required for the app to work, both of which lead to
negative user experiences.&lt;/p&gt;
&lt;p&gt;Thankfully, research by the Chrome team shows that data is very rarely
cleared automatically by Chrome. It is far more common for users to manually
clear storage. Thus if a user visits your site regularly, the chances are
small that your data will be evicted. To prevent the browser from deleting
your data, you can request that your entire site&#39;s storage be marked
persistent.&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; Requesting that all your site&#39;s data be marked as persistent should only be done for critical data (for example, end-to-end encryption keys) that if not backed up to the cloud, could result in significant data loss if not saved. Persistent storage is not deleted by the browser, even if storage is running low. It will only be deleted if the user chooses to remove it via their site settings. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;Persistent storage is &lt;a href=&quot;https://caniuse.com/#feat=mdn-api_permissions_persistent-storage_permission&quot; rel=&quot;noopener&quot;&gt;supported in many modern&lt;/a&gt; browsers.&lt;/p&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 55, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      55
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 57, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      57
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      79
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 15.2, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      15.2
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/StorageManager/persisted#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;To learn more about eviction, how much you can store, and how to handle quota
limitations, see &lt;a href=&quot;https://web.dev/storage-for-the-web/&quot;&gt;Storage for the web&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;check-if-your-sites-storage-has-been-marked-as-persistent&quot;&gt;Check if your site&#39;s storage has been marked as persistent &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#check-if-your-sites-storage-has-been-marked-as-persistent&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use JavaScript to determine if your site&#39;s storage has been marked
as persistent. Calling &lt;code&gt;navigator.storage.persisted()&lt;/code&gt; returns a Promise that
resolves with a boolean, indicating whether storage has been marked as
persisted.&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 comment&quot;&gt;// Check if site&#39;s storage has been marked as persistent&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persist&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; isPersisted &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persisted&lt;/span&gt;&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Persisted storage granted: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;isPersisted&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;h2 id=&quot;when-should-i-ask-for-persistent-storage&quot;&gt;When should I ask for persistent storage? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#when-should-i-ask-for-persistent-storage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The best time to request your storage be marked as persistent is when you
save critical user data, and the request should ideally be wrapped in a user
gesture. Do not ask for persistent storage on page load, or in other bootstrap
code, the browser may prompt the user for permission. If the user
isn&#39;t doing anything that they think needs to be saved, the prompt may be
confusing, and they&#39;ll likely reject the request. Additionally, don&#39;t prompt
too frequently. If the user decided not to grant permission, don&#39;t immediately
prompt again on the next save.&lt;/p&gt;
&lt;h2 id=&quot;request-persistent-storage&quot;&gt;Request persistent storage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#request-persistent-storage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To request persistent storage for your site&#39;s data, call
&lt;code&gt;navigator.storage.persist()&lt;/code&gt;. It returns a Promise that resolves with a
boolean, indicating whether the persistent storage permission was granted.&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 comment&quot;&gt;// Request persistent storage for site&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;navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;persist&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; isPersisted &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;persist&lt;/span&gt;&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Persisted storage granted: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;isPersisted&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The API names to &lt;em&gt;check&lt;/em&gt; if your site&#39;s storage has already been marked persistent, and to &lt;em&gt;request&lt;/em&gt; persistent storage are very similar. The way I remember the difference is &lt;code&gt;persisted()&lt;/code&gt; is past-tense, and is used to check if it&#39;s already persist&lt;strong&gt;ed&lt;/strong&gt;. Whereas &lt;code&gt;persist()&lt;/code&gt; is present-tense and asks for it now. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;how-is-permission-granted&quot;&gt;How is permission granted? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#how-is-permission-granted&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Persistent storage is treated as a &lt;a href=&quot;https://storage.spec.whatwg.org/#persistence&quot; rel=&quot;noopener&quot;&gt;permission&lt;/a&gt;. Browsers use
different factors to decide whether to grant persistent storage permissions.&lt;/p&gt;
&lt;h4 id=&quot;chrome-and-other-chromium-based-browsers&quot;&gt;Chrome and other Chromium-based browsers &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#chrome-and-other-chromium-based-browsers&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Chrome, and most other Chromium-based browsers automatically handle the
permission request, and do not show any prompts to the user. Instead, if a
site is considered important, the persistent storage permission is
automatically granted, otherwise it is silently denied.&lt;/p&gt;
&lt;p&gt;The heuristics to determine if a site is important include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How high is the level of site engagement?&lt;/li&gt;
&lt;li&gt;Has the site been installed or bookmarked?&lt;/li&gt;
&lt;li&gt;Has the site been granted permission to show notifications?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the request was denied, it can be requested again later and will be
evaluated using the same heuristics.&lt;/p&gt;
&lt;h4 id=&quot;firefox&quot;&gt;Firefox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#firefox&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Firefox delegates the permission request to the user. When persistent storage
is requested, it prompts the user with a UI popup asking if they will allow
the site to store data in persistent storage.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A popup shown by Firefox when a site requests persistent storage.&quot; decoding=&quot;async&quot; height=&quot;177&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 428px) 428px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/o8W7pNTZ5dFKeDg2cmvA.jpg?auto=format&amp;w=856 856w&quot; width=&quot;428&quot; /&gt;
  &lt;figcaption&gt;
    A popup shown by Firefox when a site requests persistent storage.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;what-storage-is-protected-by-persistent-storage&quot;&gt;What storage is protected by persistent storage? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#what-storage-is-protected-by-persistent-storage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If the persistent storage permission is granted, the browser will not evict
data stored in:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cache API&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;li&gt;DOM Storage (Local Storage)&lt;/li&gt;
&lt;li&gt;File System API (browser-provided and sandboxed file system)&lt;/li&gt;
&lt;li&gt;IndexedDB&lt;/li&gt;
&lt;li&gt;Service workers&lt;/li&gt;
&lt;li&gt;App Cache (deprecated, should not be used)&lt;/li&gt;
&lt;li&gt;WebSQL (deprecated, should not be used)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;how-to-turn-off-persistent-storage&quot;&gt;How to turn off persistent storage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#how-to-turn-off-persistent-storage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At this time, there is no programmatic way to tell the browser you no longer
need persistent storage.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Research from the Chrome team shows that although possible, stored data is
rarely cleared automatically by Chrome. To protect critical data that may
not be stored in the cloud, or will result in significant data loss,
persistent storage can be a helpful tool to ensure that your data is not
removed by the browser when the local device is faced with storage pressure.
And remember, only request persistent storage when the user is most likely to
want it.&lt;/p&gt;
&lt;h3 id=&quot;thanks&quot;&gt;Thanks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/persistent-storage/#thanks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Special thanks to Victor Costan, and Joe Medley for reviewing this article.
Thanks to Chris Wilson who wrote the original version of this article that
first appeared on WebFundamentals.&lt;/p&gt;
&lt;p&gt;Hero image by Umberto on &lt;a href=&quot;https://unsplash.com/photos/jXd2FSvcRr8&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Storage for the web</title>
    <link href="https://web.dev/storage-for-the-web/"/>
    <updated>2020-04-27T00:00:00Z</updated>
    <id>https://web.dev/storage-for-the-web/</id>
    <content type="html" mode="escaped">&lt;p&gt;Internet connections can be flakey or non-existent on the go, which is why
offline support and reliable performance are common features in
&lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;progressive web apps&lt;/a&gt;. Even in perfect wireless
environments, judicious use of caching and other storage techniques can
substantially improve the user experience. There are several ways to cache
your static application resources (HTML, JavaScript, CSS, images, etc.), and
data (user data, news articles, etc.). But which is the best solution? How
much can you store? How do you prevent it from being evicted?&lt;/p&gt;
&lt;h2 id=&quot;recommendation&quot;&gt;What should I use? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#recommendation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s a general recommendation for storing resources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For the network resources necessary to load your app and file-based content,
use the &lt;a href=&quot;https://web.dev/cache-api-quick-guide/&quot;&gt;&lt;strong&gt;Cache Storage API&lt;/strong&gt;&lt;/a&gt; (part of
&lt;a href=&quot;https://developer.chrome.com/docs/workbox/service-worker-overview/&quot; rel=&quot;noopener&quot;&gt;service workers&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;For other data, use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API&quot; rel=&quot;noopener&quot;&gt;&lt;strong&gt;IndexedDB&lt;/strong&gt;&lt;/a&gt; (with a
&lt;a href=&quot;https://www.npmjs.com/package/idb&quot; rel=&quot;noopener&quot;&gt;promises wrapper&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IndexedDB and the Cache Storage API are supported in every modern browser.
They&#39;re both asynchronous, and will not block the main thread. They&#39;re
accessible from the &lt;code&gt;window&lt;/code&gt; object, web workers, and service workers, making
it easy to use them anywhere in your code.&lt;/p&gt;
&lt;h2 id=&quot;other&quot;&gt;What about other storage mechanisms? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#other&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are several other storage mechanisms available in the browser, but they
have limited use and may cause significant performance issues.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/Window/sessionStorage&quot; rel=&quot;noopener&quot;&gt;SessionStorage&lt;/a&gt; is tab specific, and scoped to the
lifetime of the tab. It may be useful for storing small amounts of session
specific information, for example an IndexedDB key. It should be used with
caution because it is synchronous and will block the main thread. It is
limited to about 5MB and can contain only strings. Because it is tab specific,
it is not accessible from web workers or service workers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/en/docs/Web/API/Window/localStorage&quot; rel=&quot;noopener&quot;&gt;LocalStorage&lt;/a&gt; should be avoided because it is synchronous
and will block the main thread. It is limited to about 5MB and can contain
only strings. LocalStorage is not accessible from web workers or service
workers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTTP/Cookies&quot; rel=&quot;noopener&quot;&gt;Cookies&lt;/a&gt; have their uses, but should not be used for storage.
Cookies are sent with every HTTP request, so storing anything more than a
small amount of data will significantly increase the size of every web request.
They are synchronous, and are not accessible from web workers. Like
LocalStorage and SessionStorage, cookies are limited to only strings.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File_and_Directory_Entries_API/Introduction&quot; rel=&quot;noopener&quot;&gt;File System API&lt;/a&gt; and FileWriter API provide methods for
reading and writing files to a sandboxed file system. While it is asynchronous,
it is not recommended because it is
&lt;a href=&quot;https://caniuse.com/#feat=filesystem&quot; rel=&quot;noopener&quot;&gt;only available in Chromium-based browsers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/file-system-access/&quot;&gt;File System Access API&lt;/a&gt; was designed to make it
easy for users to read and edit files on their local file system. The user
must grant permission before a page can read or write to any local file, and
permissions are not persisted across sessions.&lt;/p&gt;
&lt;p&gt;WebSQL should &lt;strong&gt;not&lt;/strong&gt; be used, and existing usage should be migrated to
IndexedDB. Support has &lt;a href=&quot;https://caniuse.com/#feat=sql-storage&quot; rel=&quot;noopener&quot;&gt;been removed&lt;/a&gt; from almost all major
browsers. The W3C &lt;a href=&quot;https://www.w3.org/TR/webdatabase/&quot; rel=&quot;noopener&quot;&gt;stopped maintaining the Web SQL spec&lt;/a&gt; in 2010,
with no plans to further updates planned.&lt;/p&gt;
&lt;p&gt;Application Cache should &lt;strong&gt;not&lt;/strong&gt; be used, and existing usage should be
migrated to service workers and the Cache API. It has been
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Window/applicationCache&quot; rel=&quot;noopener&quot;&gt;deprecated&lt;/a&gt; and support will be removed from browsers in
the future.&lt;/p&gt;
&lt;h2 id=&quot;how-much&quot;&gt;How much can I store? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#how-much&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In short, &lt;strong&gt;a lot&lt;/strong&gt;, at least a couple of hundred megabytes, and potentially
hundreds of gigabytes or more. Browser implementations vary, but the amount
of storage available is usually based on the amount of storage available on the
device.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome allows the browser to use up to 80% of total disk space. An origin can
use up to 60% of the total disk space. You can use the &lt;a href=&quot;https://web.dev/storage-for-the-web/#check&quot;&gt;StorageManager
API&lt;/a&gt; to determine the maximum quota available. Other Chromium-based
browsers may be different.
&lt;ul&gt;
&lt;li&gt;In incognito mode, Chrome reduces the amount of storage an origin can use
to approximately 5% of the total disk space.&lt;/li&gt;
&lt;li&gt;If the user has enabled &amp;quot;Clear cookies and site data when you close all
windows&amp;quot; in Chrome, the storage quota is significantly reduced to a
maximum of approximately 300MB.&lt;/li&gt;
&lt;li&gt;See &lt;a href=&quot;https://github.com/GoogleChrome/web.dev/pull/3896&quot; rel=&quot;noopener&quot;&gt;PR #3896&lt;/a&gt; for
details about Chrome&#39;s implementation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Internet Explorer 10 and later can store up to 250MB and will prompt the
user when more than 10MB has been used.&lt;/li&gt;
&lt;li&gt;Firefox allows the browser to use up to 50% of free disk space. An
&lt;a href=&quot;https://godoc.org/golang.org/x/net/publicsuffix&quot; rel=&quot;noopener&quot;&gt;eTLD+1&lt;/a&gt;
group (e.g., &lt;code&gt;example.com&lt;/code&gt;, &lt;code&gt;www.example.com&lt;/code&gt; and &lt;code&gt;foo.bar.example.com&lt;/code&gt;)
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria#Storage_limits&quot; rel=&quot;noopener&quot;&gt;may use up to 2GB&lt;/a&gt;. You can use the
&lt;a href=&quot;https://web.dev/storage-for-the-web/#check-available&quot;&gt;StorageManager API&lt;/a&gt; to determine how much space is still
available.&lt;/li&gt;
&lt;li&gt;Safari (both desktop and mobile) appears to allow about 1GB. When the limit
is reached, Safari will prompt the user, increasing the limit in 200MB
increments. I was unable to find any official documentation on this.
&lt;ul&gt;
&lt;li&gt;If a PWA is added to the home screen on mobile Safari, it appears to
create a new storage container, and nothing is shared between the PWA
and mobile Safari. Once the quota has been hit for an installed PWA, there
doesn&#39;t appear to be any way to request additional storage.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the past, if a site exceeded a certain threshold of data stored, the
browser would prompt the user to grant permission to use more data. For
example, if the origin used more than 50MB, the browser would prompt the user
to allow it to store up to 100MB, then ask again at 50MB increments.&lt;/p&gt;
&lt;p&gt;Today, most modern browsers will not prompt the user, and will allow a site
to use up to its allotted quota. The exception appears to be Safari, which
prompts when the storage quota is exceeded, requesting permission to increase
the allocated quota. If an origin
attempts to use more than its allotted quota, further attempts to write data
will fail.&lt;/p&gt;
&lt;h2 id=&quot;check&quot;&gt;How can I check how much storage is available? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#check&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In &lt;a href=&quot;https://caniuse.com/#feat=mdn-api_storagemanager&quot; rel=&quot;noopener&quot;&gt;many browsers&lt;/a&gt;, you can use the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/StorageManager/estimate&quot; rel=&quot;noopener&quot;&gt;StorageManager API&lt;/a&gt; to determine the amount of storage
available to the origin, and how much storage it&#39;s using. It reports the total
number of bytes used by IndexedDB and the Cache API, and makes it possible
to calculate the approximate remaining storage space available.&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;storage &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;estimate&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; quota &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;storage&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;estimate&lt;/span&gt;&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 comment&quot;&gt;// quota.usage -&gt; Number of bytes used.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// quota.quota -&gt; Maximum number of bytes available.&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; percentageUsed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;quota&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;usage &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; quota&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quota&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;100&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;You&#39;ve used &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;percentageUsed&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;% of the available storage.&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;const&lt;/span&gt; remaining &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; quota&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;quota &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; quota&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;usage&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;You can write up to &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;remaining&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; more bytes.&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;The StorageManager isn&#39;t &lt;a href=&quot;https://caniuse.com/#feat=mdn-api_storagemanager&quot; rel=&quot;noopener&quot;&gt;implemented&lt;/a&gt; in all browsers yet, so you
must feature detect it before using it. Even when it is available, you must
still catch over-quota errors (see below). In some cases, it&#39;s possible for
the available quota to exceed the actual amount of storage available.&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; Other Chromium-based browsers may factor in the amount of free space when reporting the available quota. Chrome does not, and will always report 60% of the actual disk size. This helps to reduce the ability to determine the size of stored cross origin resources. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;inspect&quot;&gt;Inspect &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#inspect&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;During development, you can use your browser&#39;s DevTools to inspect the
different storage types, and easily clear all stored data.&lt;/p&gt;
&lt;p&gt;A new feature was added in Chrome 88 that lets you override the site&#39;s storage
quota in the Storage Pane. This feature gives you the ability to simulate
different devices and test the behavior of your apps in low disk availability
scenarios. Go to &lt;strong&gt;Application&lt;/strong&gt; then &lt;strong&gt;Storage&lt;/strong&gt;, enable the
&lt;strong&gt;Simulate custom storage quota&lt;/strong&gt; checkbox, and enter any valid number to
simulate the storage quota.&lt;/p&gt;
&lt;img alt=&quot;DevTools Storage pane.&quot; decoding=&quot;async&quot; height=&quot;567&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/0g2WvpbGRGdVs0aAPc6ObG7gkud2/tYlbklNwF6DFKNV2cY0D.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;p&gt;While working on this article, I wrote a &lt;a href=&quot;https://storage-quota.glitch.me/&quot; rel=&quot;noopener&quot;&gt;simple tool&lt;/a&gt; to
attempt to quickly use as much storage as possible. It&#39;s a quick and easy way
to experiment with different storage mechanisms, and see what happens when
you use all of your quota.&lt;/p&gt;
&lt;h2 id=&quot;over-quota&quot;&gt;How to handle going over quota? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#over-quota&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What should you do when you go over quota? Most importantly, you should
always catch and handle write errors, whether it&#39;s a &lt;code&gt;QuotaExceededError&lt;/code&gt; or
something else. Then, depending on your app design, decide how to handle it.
For example delete content that hasn&#39;t been accessed in a long time, remove
data based on size, or provide a way for users to choose what they want to delete.&lt;/p&gt;
&lt;p&gt;Both IndexedDB and the Cache API both throw a &lt;code&gt;DOMError&lt;/code&gt; named
&lt;code&gt;QuotaExceededError&lt;/code&gt; when you&#39;ve exceeded the quota available.&lt;/p&gt;
&lt;h3 id=&quot;indexeddb&quot;&gt;IndexedDB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#indexeddb&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If the origin has exceeded its quota, attempts to write to IndexedDB will
fail. The transaction&#39;s &lt;code&gt;onabort()&lt;/code&gt; handler will be called, passing an event.
The event will include a &lt;code&gt;DOMException&lt;/code&gt; in the error property. Checking the
error &lt;code&gt;name&lt;/code&gt; will return &lt;code&gt;QuotaExceededError&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;const&lt;/span&gt; transaction &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; idb&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transaction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;entries&#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 string&quot;&gt;&#39;readwrite&#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;transaction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;onabort&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;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;const&lt;/span&gt; error &lt;span class=&quot;token operator&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;error&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DOMException&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;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;QuotaExceededError&#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;// Fallback code goes here&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;cache-api&quot;&gt;Cache API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#cache-api&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If the origin has exceeded its quota, attempts to write to the Cache API
will reject with a &lt;code&gt;QuotaExceededError&lt;/code&gt; &lt;code&gt;DOMException&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;try&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; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;my-cache&#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;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/sample1.jpg&#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;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&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;error&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;QuotaExceededError&#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;// Fallback code goes here&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;h2 id=&quot;eviction&quot;&gt;How does eviction work? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#eviction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Web storage is categorized into two buckets, &amp;quot;Best Effort&amp;quot; and &amp;quot;Persistent&amp;quot;.
Best effort means the storage can be cleared by the browser without
interrupting the user, but is less durable for long-term or critical data.
Persistent storage is not automatically cleared when storage is low. The user
needs to manually clear this storage (via browser settings).&lt;/p&gt;
&lt;p&gt;By default, a site&#39;s data (including IndexedDB, Cache API, etc) falls into
the best effort category, which means unless a site has
&lt;a href=&quot;https://web.dev/persistent-storage/&quot;&gt;requested persistent storage&lt;/a&gt;, the browser may evict
site data at its discretion, for example, when device storage is low.&lt;/p&gt;
&lt;p&gt;The eviction policy for best effort is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chromium-based browsers will begin to evict data when the browser runs out
of space, clearing all site data from the least recently used origin first,
then the next, until the browser is no longer over the limit.&lt;/li&gt;
&lt;li&gt;Internet Explorer 10+ will not evict data, but will prevent the origin from
writing any more.&lt;/li&gt;
&lt;li&gt;Firefox will begin to evict data when the available disk space is filled up,
clearing all site data from the least recently used origin first, then the
next, until the browser is no longer over the limit.&lt;/li&gt;
&lt;li&gt;Safari previously did not evict data, but recently implemented a new
seven-day cap on all writable storage (see below).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Starting in iOS and iPadOS 13.4 and Safari 13.1 on macOS, there is a
seven-day cap on all script writable storage, including IndexedDB, service
worker registration, and the Cache API. This means Safari will evict all
content from the cache after seven days of Safari use if the user does not
interact with the site. This eviction policy &lt;strong&gt;does not apply to installed
PWAs&lt;/strong&gt; that have been added to the home screen. See
&lt;a href=&quot;https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/&quot; rel=&quot;noopener&quot;&gt;Full Third-Party Cookie Blocking and More&lt;/a&gt; on the WebKit
blog for complete details.&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; You can request &lt;a href=&quot;https://web.dev/persistent-storage/&quot;&gt;persistent storage&lt;/a&gt; for your site to protect critical user or application data. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;bonus-why-use-a-wrapper-for-indexeddb&quot;&gt;Bonus: Why use a wrapper for IndexedDB &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#bonus-why-use-a-wrapper-for-indexeddb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;IndexedDB is a low level API that requires significant setup before use,
which can be particularly painful for storing simple data.  Unlike most modern
promise-based APIs, it is event based. Promise wrappers like
&lt;a href=&quot;https://www.npmjs.com/package/idb&quot; rel=&quot;noopener&quot;&gt;idb&lt;/a&gt; for IndexedDB hide some of the powerful features but more
importantly, hide the complex machinery (e.g. transactions, schema versioning)
that comes with the IndexedDB library.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Gone are the days of limited storage and prompting the user to store more and
more data. Sites can store effectively all of the resources and data they
need to run. Using the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/StorageManager/estimate&quot; rel=&quot;noopener&quot;&gt;StorageManager API&lt;/a&gt; you can
determine how much is available to you, and how much you&#39;ve used. And with
&lt;a href=&quot;https://web.dev/persistent-storage/&quot;&gt;persistent storage&lt;/a&gt;, unless the user removes it, you
can protect it from eviction.&lt;/p&gt;
&lt;h3 id=&quot;additional-resources&quot;&gt;Additional resources &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#additional-resources&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/indexeddb-best-practices/&quot;&gt;IndexedDB Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.google.com/document/d/19QemRTdIxYaJ4gkHYf2WWBNPbpuZQDNMpUVf8dQxj4U/preview&quot; rel=&quot;noopener&quot;&gt;Chrome Web Storage and Quota Concepts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;thanks&quot;&gt;Thanks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/storage-for-the-web/#thanks&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Special thanks to Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy,
Darwin Huang, Josh Bell, Marijn Kruisselbrink, and Victor Costan for reviewing
this article. Thanks to Eiji Kitamura, Addy Osmani, and Marc Cohen who wrote
the original articles that this is based on. Eiji wrote a helpful tool
called &lt;a href=&quot;http://demo.agektmr.com/storage/&quot; rel=&quot;noopener&quot;&gt;Browser Storage Abuser&lt;/a&gt; that was useful in validating
current behavior. It allows you to store as much data as possible and see the
storage limits on your browser. Thanks to Francois Beaufort who did the digging
into Safari to figure out its storage limits.&lt;/p&gt;
&lt;p&gt;The hero image is by Guillaume Bolduc on
&lt;a href=&quot;https://unsplash.com/photos/uBe2mknURG4&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>What does it take to be installable?</title>
    <link href="https://web.dev/install-criteria/"/>
    <updated>2020-02-14T00:00:00Z</updated>
    <id>https://web.dev/install-criteria/</id>
    <content type="html" mode="escaped">&lt;p&gt;Progressive Web Apps (PWAs) are modern, high quality applications built
using web technology. PWAs offer similar capabilities to iOS/Android/desktop apps, they
are reliable even in unstable network conditions, and are installable making
it easier for users to find and use them.&lt;/p&gt;
&lt;p&gt;Most users are familiar with installing applications, and the benefits of an
installed experience. Installed applications appear on operating system
launch surfaces, such as the Applications folder on Mac OS X, the Start menu
on Windows, and the home screen on Android and iOS. Installed applications
also show up in the activity switcher, device search engines such as
Spotlight and in content sharing sheets.&lt;/p&gt;
&lt;p&gt;Most browsers indicate to the user that your Progressive Web App (PWA) is
installable when it meets certain criteria. Example indicators include an
Install button in the address bar, or an Install menu item in the overflow
menu.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
  &lt;figure id=&quot;browser-install-promo&quot;&gt;
    &lt;img alt=&quot;Screenshot of omnibox with install indicator visible.&quot; decoding=&quot;async&quot; height=&quot;307&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/O9KXz4aQXm3ZOzPo98uT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      Browser provided install promotion (desktop)
    &lt;/figcaption&gt;
  &lt;/figure&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Screenshot of browser provided install promotion.&quot; decoding=&quot;async&quot; height=&quot;307&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/bolh05TCEeT7xni4eUTG.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
    &lt;figcaption&gt;
      Browser provided install promotion (mobile)
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;In addition, when the criteria is met, many browsers will fire a
&lt;code&gt;beforeinstallprompt&lt;/code&gt; event, allowing you to provide a custom in-app UX that
that will trigger the install flow within your app.&lt;/p&gt;
&lt;h2 id=&quot;criteria&quot;&gt;Install criteria &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/install-criteria/#criteria&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In Chrome, your Progressive Web App must meet the following criteria before
it will fire the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event and show the in-browser install
promotion:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The web app is not already installed&lt;/li&gt;
&lt;li&gt;Meets the user engagement heuristics:
&lt;ul&gt;
&lt;li&gt;The user needs to have clicked or tapped on the page at least once (at any time, even during a previous page load)&lt;/li&gt;
&lt;li&gt;The user needs to have spent at least 30 seconds viewing the page (at any time)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Be served over HTTPS&lt;/li&gt;
&lt;li&gt;Includes a &lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;web app manifest&lt;/a&gt; that includes:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;short_name&lt;/code&gt; or &lt;code&gt;name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;icons&lt;/code&gt; - must include a 192px and a 512px icon&lt;/li&gt;
&lt;li&gt;&lt;code&gt;start_url&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;display&lt;/code&gt; - must be one of &lt;code&gt;fullscreen&lt;/code&gt;, &lt;code&gt;standalone&lt;/code&gt;, or &lt;code&gt;minimal-ui&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prefer_related_applications&lt;/code&gt; must not be present, or be &lt;code&gt;false&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Other browsers have similar criteria for installation, though there may be
minor differences. Check the respective sites for full details:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/microsoft-edge/progressive-web-apps#requirements&quot; rel=&quot;noopener&quot;&gt;Edge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/Progressive_web_apps/Installable_PWAs&quot; rel=&quot;noopener&quot;&gt;Firefox&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dev.opera.com/articles/installable-web-apps/&quot; rel=&quot;noopener&quot;&gt;Opera&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; On Android, if the web app manifest includes &lt;code&gt;related_applications&lt;/code&gt; and &lt;code&gt;&amp;quot;prefer_related_applications&amp;quot;: true&lt;/code&gt;, the user will be directed to the Google Play store and &lt;a href=&quot;https://developer.chrome.com/blog/app-install-banners-native/&quot;&gt;prompted to install the specified Android app&lt;/a&gt; instead. &lt;/div&gt;&lt;/aside&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>How to provide your own in-app install experience</title>
    <link href="https://web.dev/customize-install/"/>
    <updated>2020-02-14T00:00:00Z</updated>
    <id>https://web.dev/customize-install/</id>
    <content type="html" mode="escaped">&lt;p&gt;Many browsers make it possible for you to enable and promote the installation
of your Progressive Web App (PWA) directly within the user interface of your
PWA. Installation (sometimes formerly referred to as Add to Home Screen),
makes it easy for users to install your PWA on their mobile or desktop device.
Installing a PWA adds it to a user&#39;s launcher, allowing it to be run like any
other installed app.&lt;/p&gt;
&lt;p&gt;In addition to the &lt;a href=&quot;https://web.dev/promote-install/#browser-promotion&quot;&gt;browser provided install experience&lt;/a&gt;,
it&#39;s possible to provide your own custom install flow, directly within your app.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Install App button provided in the Spotify PWA&quot; decoding=&quot;async&quot; height=&quot;550&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 491px) 491px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/SW3unIBfyMRTZNK0DRIw.png?auto=format&amp;w=982 982w&quot; width=&quot;491&quot; /&gt;
  &lt;figcaption&gt;
    &quot;Install App&quot; button provided in the Spotify PWA
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When considering whether to promote install, it&#39;s best to think about how
users typically use your PWA.  For example, if there&#39;s a set of users who
use your PWA multiple times in a week, these users might benefit from the
added convenience of launching your app from a smartphone homescreen or
from the Start menu in a desktop operating system.  Some productivity and
entertainment applications also benefit from the extra screen real-estate
created by removing the browser toolbars from the window in installed
&lt;code&gt;standalone&lt;/code&gt; or &lt;code&gt;minimal-ui&lt;/code&gt; modes.&lt;/p&gt;
&lt;h2 id=&quot;promote-installation&quot;&gt;Promoting installation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#promote-installation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To indicate your Progressive Web App is installable, and to provide a custom
in-app install flow:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Listen for the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;Save the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event, so it can be used to trigger the
install flow later.&lt;/li&gt;
&lt;li&gt;Alert the user that your PWA is installable, and provide a button or other
element to start the in-app installation flow.&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;beforeinstallprompt&lt;/code&gt; event, and the &lt;code&gt;appinstalled&lt;/code&gt; event have been moved from the web app manifest spec to their own &lt;a href=&quot;https://github.com/WICG/beforeinstallprompt&quot;&gt;incubator&lt;/a&gt;. The Chrome team remains committed to supporting them, and has no plans to remove or deprecate support. Google&#39;s Web DevRel team continues to recommend using them to provide a customized install experience. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;beforeinstallprompt&quot;&gt;Listen for the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#beforeinstallprompt&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If your Progressive Web App meets the required &lt;a href=&quot;https://web.dev/install-criteria/&quot;&gt;installation criteria&lt;/a&gt;,
the browser fires a &lt;code&gt;beforeinstallprompt&lt;/code&gt; event. Save a reference to the
event, and update your user interface to indicate that the user can install
your PWA. This is highlighted below.&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 comment&quot;&gt;// Initialize deferredPrompt for use later to show browser install prompt.&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; deferredPrompt&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeinstallprompt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Prevent the mini-infobar from appearing on mobile&lt;/span&gt;&lt;br /&gt;  e&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 comment&quot;&gt;// Stash the event so it can be triggered later.&lt;/span&gt;&lt;br /&gt;  deferredPrompt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Update UI notify the user they can install the PWA&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;showInstallPromotion&lt;/span&gt;&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 comment&quot;&gt;// Optionally, send analytics event that PWA install promo was shown.&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeinstallprompt&#39; event was fired.&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;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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; There are many different &lt;a href=&quot;https://web.dev/promote-install/&quot;&gt;patterns&lt;/a&gt; that you can use to notify the user your app can be installed and provide an in-app install flow, for example, a button in the header, an item in the navigation menu, or an item in your content feed. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;in-app-flow&quot;&gt;In-app installation flow &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#in-app-flow&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To provide in-app installation, provide a button or other interface element
that a user can click to install your app. When the element is
clicked, call &lt;code&gt;prompt()&lt;/code&gt; on the saved &lt;code&gt;beforeinstallprompt&lt;/code&gt; event (stored
in the &lt;code&gt;deferredPrompt&lt;/code&gt; variable). It shows the user a modal install dialog,
asking them to confirm they want to install your PWA.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;buttonInstall&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Hide the app provided install promotion&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;hideInstallPromotion&lt;/span&gt;&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 comment&quot;&gt;// Show the install prompt&lt;/span&gt;&lt;br /&gt;  deferredPrompt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prompt&lt;/span&gt;&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 comment&quot;&gt;// Wait for the user to respond to the prompt&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; outcome &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;await&lt;/span&gt; deferredPrompt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userChoice&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Optionally, send analytics event with outcome of user choice&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;User response to the install prompt: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;outcome&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 comment&quot;&gt;// We&#39;ve used the prompt, and can&#39;t use it again, throw it away&lt;/span&gt;&lt;br /&gt;  deferredPrompt &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;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;userChoice&lt;/code&gt; property is a promise that resolves with the user&#39;s choice.
You can only call &lt;code&gt;prompt()&lt;/code&gt; on the deferred event once. If the user
dismisses it, you&#39;ll need to wait until the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event
is fired again, typically immediately after the &lt;code&gt;userChoice&lt;/code&gt; property
has resolved.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-quaternary-box-bg color-quaternary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &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;Code brackets&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;M9.41 16.59L8 18l-6-6 6-6 1.41 1.41L4.83 12l4.58 4.59zm5.18-9.18L16 6l6 6-6 6-1.41-1.41L19.17 12l-4.58-4.59z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Try it&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; &lt;a href=&quot;https://web.dev/codelab-make-installable&quot;&gt;Make a site installable using the beforeinstallprompt event&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;detect-install&quot;&gt;Detect when the PWA was successfully installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#detect-install&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can use the &lt;code&gt;userChoice&lt;/code&gt; property to determine if the user installed
your app from within your user interface. But, if the user installs your
PWA from the address bar or other browser component, &lt;code&gt;userChoice&lt;/code&gt; won&#39;t
help. Instead, you should listen for the &lt;code&gt;appinstalled&lt;/code&gt; event. It is fired
whenever your PWA is installed, no matter what mechanism is used to install
your PWA.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;appinstalled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Hide the app-provided install promotion&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token function&quot;&gt;hideInstallPromotion&lt;/span&gt;&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 comment&quot;&gt;// Clear the deferredPrompt so it can be garbage collected&lt;/span&gt;&lt;br /&gt;  deferredPrompt &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 comment&quot;&gt;// Optionally, send analytics event to indicate successful install&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;PWA was installed&#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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;detect-launch-type&quot;&gt;Detect how the PWA was launched &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#detect-launch-type&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The CSS &lt;code&gt;display-mode&lt;/code&gt; media query indicates how the PWA was launched,
either in a browser tab, or as an installed PWA. This makes it possible to
apply different styles depending on how the app was launched. For example,
always hide the install button and provide a back button when launched as an
installed PWA.&lt;/p&gt;
&lt;h3 id=&quot;track-how-the-pwa-was-launched&quot;&gt;Track how the PWA was launched &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#track-how-the-pwa-was-launched&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To track how users launch your PWA, use &lt;code&gt;matchMedia()&lt;/code&gt; to test the
&lt;code&gt;display-mode&lt;/code&gt; media query. Safari on iOS doesn&#39;t support
this yet, so you must check &lt;code&gt;navigator.standalone&lt;/code&gt;, it returns a boolean
indicating whether the browser is running in standalone mode.&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPWADisplayMode&lt;/span&gt;&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; isStandalone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(display-mode: standalone)&#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;matches&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;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;referrer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;android-app://&#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;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;twa&#39;&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 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;standalone &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; isStandalone&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 string&quot;&gt;&#39;standalone&#39;&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;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;browser&#39;&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;h3 id=&quot;track-when-the-display-mode-changes&quot;&gt;Track when the display mode changes &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#track-when-the-display-mode-changes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To track if the user changes between &lt;code&gt;standalone&lt;/code&gt;, and &lt;code&gt;browser tab&lt;/code&gt;, listen for
changes to the &lt;code&gt;display-mode&lt;/code&gt; media query.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchMedia&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;(display-mode: standalone)&#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;addEventListener&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;span class=&quot;token parameter&quot;&gt;evt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; displayMode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;browser&#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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;matches&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;    displayMode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;standalone&#39;&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 comment&quot;&gt;// Log display mode change to analytics&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;DISPLAY_MODE_CHANGED&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; displayMode&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;h3 id=&quot;update-ui-based-on-the-current-display-mode&quot;&gt;Update UI based on the current display mode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#update-ui-based-on-the-current-display-mode&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To apply a different background color for a PWA when launched as an installed
PWA, use conditional CSS:&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;@media&lt;/span&gt; all &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;display-mode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; standalone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; yellow&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;h2 id=&quot;updating-your-apps-icon-and-name&quot;&gt;Updating your app&#39;s icon and name &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/customize-install/#updating-your-apps-icon-and-name&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What if you need to update your app name, or provide new icons?
Check out &lt;a href=&quot;https://web.dev/manifest-updates/&quot;&gt;How Chrome handles updates to the web app manifest&lt;/a&gt;
to see when and how are those changes are reflected in Chrome.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>What makes a good Progressive Web App?</title>
    <link href="https://web.dev/pwa-checklist/"/>
    <updated>2020-01-06T00:00:00Z</updated>
    <id>https://web.dev/pwa-checklist/</id>
    <content type="html" mode="escaped">&lt;!-- Disable heading-increment because it mucks with the Details widget --&gt;
&lt;!--lint disable heading-increment--&gt;
&lt;p&gt;Progressive Web Apps (PWA) are built and enhanced with modern APIs to deliver
enhanced capabilities, reliability, and installability while reaching
&lt;em&gt;anyone, anywhere, on any device&lt;/em&gt; with a single codebase. To help you create
the best possible experience, use the &lt;a href=&quot;https://web.dev/pwa-checklist/#core&quot;&gt;core&lt;/a&gt; and &lt;a href=&quot;https://web.dev/pwa-checklist/#optimal&quot;&gt;optimal&lt;/a&gt;
checklists and recommendations to guide you.&lt;/p&gt;
&lt;h2 id=&quot;core&quot;&gt;Core Progressive Web App checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#core&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Progressive Web App Checklist describes what makes an app installable and
usable by all users, regardless of size or input type.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  Starts fast, stays fast
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Performance plays a significant role in the success of any online experience, because high performing sites engage and retain users better than poorly performing ones. Sites should focus on optimizing for user-centric performance metrics.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Performance plays a significant role in the success of any online experience,
because high performing sites engage and retain users better than poorly
performing ones. Sites should focus on optimizing for user-centric performance metrics.&lt;/p&gt;
&lt;h4 id=&quot;why&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Speed is critical for getting users to &lt;em&gt;use&lt;/em&gt; your app.
In fact, as page load times go from one second to ten seconds, the probability of
a user bouncing increases by 123%. Performance doesn&#39;t stop with the &lt;code&gt;load&lt;/code&gt; event.
Users should never wonder whether their interaction—for example, clicking a
button—was registered or not. Scrolling and animation should feel smooth.
Performance affects your entire experience, from how users perceive your
application to how it actually performs.&lt;/p&gt;
&lt;p&gt;While all applications have different needs, the performance audits in
Lighthouse are based on the
&lt;a href=&quot;https://web.dev/vitals/&quot;&gt;Core Web Vitals&lt;/a&gt;,
and scoring high on those audits will make it more likely that your users have
an enjoyable experience. You can also use
&lt;a href=&quot;https://pagespeed.web.dev/&quot; rel=&quot;noopener&quot;&gt;PageSpeed Insights&lt;/a&gt;
or the &lt;a href=&quot;https://developer.chrome.com/docs/crux/&quot; rel=&quot;noopener&quot;&gt;Chrome User Experience Report&lt;/a&gt;
to get real-world performance data for your web app.&lt;/p&gt;
&lt;h4 id=&quot;how&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Follow our &lt;a href=&quot;https://web.dev/fast/&quot;&gt;guide on fast load times&lt;/a&gt; to learn how to make your PWA
start fast and stay fast.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Works in any  browser
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Users can use any browser they choose to access your web app before it&#39;s installed.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Users can use any browser they choose to access your web app before it&#39;s
installed.&lt;/p&gt;
&lt;h4 id=&quot;why-2&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Progressive Web Apps are web apps first, and that means they need to work
across browsers, not just in one of them.&lt;/p&gt;
&lt;p&gt;An effective way of doing this is by, in the words of Jeremy Keith in
&lt;a href=&quot;https://resilientwebdesign.com/&quot; rel=&quot;noopener&quot;&gt;Resilient Web Design&lt;/a&gt;, identifying the core
functionality, making that functionality available using the simplest possible
technology, and then enhancing the experience where possible. In many cases,
this means starting with just HTML to create the core functionality, and
enhancing the user experience with CSS and JavaScript to create a more
engaging experience.&lt;/p&gt;
&lt;p&gt;Take form submission for example. The simplest way to implement that is an
HTML form that submits a &lt;code&gt;POST&lt;/code&gt; request. After building that, you can enhance
the experience with JavaScript to do form validation and submit the form via
AJAX, improving the experience for users who can support it.&lt;/p&gt;
&lt;p&gt;Consider that your users will experience your site across a spectrum of
devices and browsers. You cannot simply target the top end of the spectrum.
By using feature detection, you&#39;ll be able to deliver a usable experience
for the widest range of potential users, including those using browsers
and devices that may not exist today.&lt;/p&gt;
&lt;h4 id=&quot;how-2&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Jeremy Keith&#39;s &lt;a href=&quot;https://resilientwebdesign.com/&quot; rel=&quot;noopener&quot;&gt;Resilient Web Design&lt;/a&gt; is an
excellent resource describing how to think about web design in this
cross-browser, progressive methodology.&lt;/p&gt;
&lt;h4 id=&quot;additional-reading&quot;&gt;Additional reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#additional-reading&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;A List Apart&#39;s &lt;a href=&quot;https://alistapart.com/article/understandingprogressiveenhancement/&quot; rel=&quot;noopener&quot;&gt;Understanding Progressive Enhancement&lt;/a&gt;
is a good introduction to the topic.&lt;/li&gt;
&lt;li&gt;Smashing Magazine&#39;s
&lt;a href=&quot;https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/&quot; rel=&quot;noopener&quot;&gt;Progressive Enhancement: What It Is, And How To Use It?&lt;/a&gt;
gives a practical introduction and links to more advanced topics.&lt;/li&gt;
&lt;li&gt;MDN has an article titled
&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/Tools_and_testing/Cross_browser_testing/Feature_detection&quot; rel=&quot;noopener&quot;&gt;Implementing feature detection&lt;/a&gt;
that talks about how to detect a feature  by directly querying it.&lt;/li&gt;
&lt;/ul&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Responsive to any screen size
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Users can use your PWA on any screen size and all of the content is available at any viewport size.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Users can use your PWA on any screen size and all of the content is available
at any viewport size.&lt;/p&gt;
&lt;h4 id=&quot;why-3&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Devices come in a range of sizes, and users may use your application at a
range of sizes, even on the same device. Therefore, it&#39;s critical to ensure
your content not only fits within the viewport, but that all features and
content for your site are usable at all viewport sizes.&lt;/p&gt;
&lt;p&gt;The tasks users want to complete and the content they want to access do
not change with viewport size. The content can be rearranged at different
viewport sizes, and it should all be there, one way or another. In fact, as
Luke Wroblewski states in his book Mobile First, starting small and going
large instead of the other way around can actually improve a site&#39;s design:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Mobile devices require software development teams to focus on only the
most important data and actions in an application. There simply isn&#39;t room
in a 320 by 480 pixel screen for extraneous, unnecessary elements.
You have to prioritize.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;how-3&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-3&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are many  resources on responsive design, including the
&lt;a href=&quot;https://alistapart.com/article/responsive-web-design/&quot; rel=&quot;noopener&quot;&gt;original article by Ethan Marcotte&lt;/a&gt;,
a &lt;a href=&quot;https://snugug.com/musings/principles-responsive-web-design/&quot; rel=&quot;noopener&quot;&gt;collection of important concepts&lt;/a&gt;
related to it, as well as books and talks galore. To narrow this discussion
down to the content aspects of responsive design, you can dig into
&lt;a href=&quot;https://uxdesign.cc/why-you-should-design-the-content-first-for-better-experiences-374f4ba1fe3c&quot; rel=&quot;noopener&quot;&gt;content-first design&lt;/a&gt;
and &lt;a href=&quot;https://alistapart.com/article/content-out-layout/&quot; rel=&quot;noopener&quot;&gt;content-out responsive layouts&lt;/a&gt;.
Finally, while it&#39;s focused on mobile, the lessons in
&lt;a href=&quot;https://www.forbes.com/sites/anthonykosner/2012/05/03/seven-deadly-mobile-myths-josh-clark-debunks-the-desktop-paradigm-and-more/#21ecac977bca&quot; rel=&quot;noopener&quot;&gt;Seven Deadly Mobile Myths&lt;/a&gt;
by Josh Clark are just as relevant to small-sized views of responsive sites
as they are to mobile.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Provides a custom offline page
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; When users are offline, keeping them in your PWA provides a more seamless experience than dropping back to the default browser offline page.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;When users are offline, keeping them in your PWA provides a more seamless
experience than dropping back to the default browser offline
page.&lt;/p&gt;
&lt;h4 id=&quot;why-4&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Users expect installed apps to work regardless of their connection status.
A platform-specific app never shows a blank page when it is offline, and a Progressive
Web App should never show the browser default offline page. Providing a
custom offline experience, both when a user navigates to a URL that hasn&#39;t
been cached and when a user tries to use a feature that requires a connection,
helps your web experience feel like it&#39;s part of the device it&#39;s running on.&lt;/p&gt;
&lt;h4 id=&quot;how-4&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-4&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;During a service worker&#39;s &lt;code&gt;install&lt;/code&gt; event, you can precache a custom offline
page for later use. If a user goes offline, you can respond with the precached
custom offline page. You can follow our custom
&lt;a href=&quot;https://googlechrome.github.io/samples/service-worker/custom-offline-page/&quot; rel=&quot;noopener&quot;&gt;offline page sample&lt;/a&gt;
to see an example of this in action and learn how to implement it yourself.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Is installable
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Users who install or add apps to their device tend to engage with those apps more.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Users who install or add apps to their device tend to engage with those
apps more.&lt;/p&gt;
&lt;h4 id=&quot;why-5&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-5&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Installing a Progressive Web App allows it to look, feel, and behave like all
other installed apps. It&#39;s launched from the same place users launch their
other apps. It runs in its own app window, separate from the browser, and it
appears in the task list, just like other apps.&lt;/p&gt;
&lt;p&gt;Why would you want a user to install your PWA? The same reason you&#39;d want a
user to install your app from an app store. Users who install your apps
are your most engaged audience, and have better engagement metrics than
typical visitors, often at parity with app users on mobile devices.
These metrics include more repeat visits, longer times on your site and
higher conversion rates.&lt;/p&gt;
&lt;h4 id=&quot;how-5&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-5&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can follow our &lt;a href=&quot;https://web.dev/customize-install/&quot;&gt;installable guide&lt;/a&gt; to learn how to make
your PWA installable, how to test to see that it&#39;s installable, and try
doing it yourself.&lt;/p&gt;
&lt;/details&gt;
&lt;h2 id=&quot;optimal&quot;&gt;Optimal Progressive Web App checklist &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#optimal&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To create a truly great Progressive Web App, one that feels like a
best-in-class app, you need more than just the core checklist. The optimal
Progressive Web App checklist is about making your PWA feel like it&#39;s part
of the device it&#39;s running on while taking advantage of what makes the web powerful.&lt;/p&gt;
&lt;details&gt;
&lt;summary&gt;
  Provides an offline experience
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Where connectivity isn&#39;t strictly required, your app works the same offline as it does online.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Where connectivity isn&#39;t strictly required, your app works the same offline
as it does online.&lt;/p&gt;
&lt;h4 id=&quot;why-6&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-6&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to providing a custom offline page, users expect Progressive Web
Apps to be usable offline. For example, travel and airline apps should have
trip details and boarding passes easily available when offline. Music,
video, and podcasting apps should allow for offline playback. Social and
news apps should cache recent content so users can read it when offline.
Users also expect to stay authenticated when offline, so design for
offline authentication. An offline PWA provides a true app-like experience
for users.&lt;/p&gt;
&lt;h4 id=&quot;how-6&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-6&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;After determining which features your users expect to work offline, you&#39;ll
need to make your content available and adaptable to offline contexts. In
addition, you can use
&lt;a href=&quot;https://web.dev/indexeddb/&quot;&gt;IndexedDB&lt;/a&gt;,
an in-browser NoSQL storage system, to store and retrieve data, and
&lt;a href=&quot;https://developer.chrome.com/blog/background-sync/&quot; rel=&quot;noopener&quot;&gt;background sync&lt;/a&gt;
to allow users to take actions while offline and defer server communications
until the user has a stable connection again. You can also use service
workers to store other kinds of content, such as images, video files, and
audio files for offline use, as well as use them to implement
&lt;a href=&quot;https://developer.chrome.com/blog/2-cookie-handoff/&quot; rel=&quot;noopener&quot;&gt;safe, long-lived sessions&lt;/a&gt;
to keep users authenticated. From a user experience perspective, you can use
&lt;a href=&quot;https://uxdesign.cc/what-you-should-know-about-skeleton-screens-a820c45a571a&quot; rel=&quot;noopener&quot;&gt;skeleton screens&lt;/a&gt;
that give users a perception of speed and content while loading that can
then fall back to cached content or an offline indicator as needed.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Is fully accessible
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; All user interactions pass &lt;a href=&quot;https://www.w3.org/TR/WCAG20/&quot;&gt;WCAG 2.0&lt;/a&gt; accessibility requirements.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;All user interactions pass &lt;a href=&quot;https://www.w3.org/TR/WCAG20/&quot; rel=&quot;noopener&quot;&gt;WCAG 2.0&lt;/a&gt;
accessibility requirements.&lt;/p&gt;
&lt;h4 id=&quot;why-7&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-7&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Most people at some point in their life will want to take advantage of your
PWA in a way that is covered under the &lt;a href=&quot;https://www.w3.org/TR/WCAG20/&quot; rel=&quot;noopener&quot;&gt;WCAG 2.0&lt;/a&gt;
accessibility requirements. Humans&#39; ability to interact with and understand
your PWA exist on a spectrum and needs can be temporary or permanent. By
making your PWA accessible, you ensure it&#39;s usable for everyone.&lt;/p&gt;
&lt;h4 id=&quot;how-7&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-7&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;W3C&#39;s &lt;a href=&quot;https://www.w3.org/WAI/fundamentals/accessibility-intro/&quot; rel=&quot;noopener&quot;&gt;Introduction to Web Accessibility&lt;/a&gt;
is a good place to start. A majority of accessibility testing
must be done manually. Tools like the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/accessibility/&quot; rel=&quot;noopener&quot;&gt;Accessibility&lt;/a&gt;
audits in Lighthouse, &lt;a href=&quot;https://github.com/dequelabs/axe-core&quot; rel=&quot;noopener&quot;&gt;axe&lt;/a&gt;, and
&lt;a href=&quot;https://accessibilityinsights.io/&quot; rel=&quot;noopener&quot;&gt;Accessibility Insights&lt;/a&gt; can help you
automate some accessibility testing. It&#39;s also important to use semantically
correct elements instead of recreating those elements on your own,
for instance, &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;button&lt;/code&gt; elements. This ensures that when you do need
to build more advanced functionality, accessible expectations are met (such as
when to use arrows vs. tabs).
&lt;a href=&quot;https://accessibilityinsights.io/&quot; rel=&quot;noopener&quot;&gt;A11Y Nutrition Cards&lt;/a&gt; has excellent
advice on this for some common components.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Can be discovered through search
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Your PWA can be easily &lt;a href=&quot;https://web.dev/discoverable/&quot;&gt;discovered through search&lt;/a&gt;.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Your PWA can be easily &lt;a href=&quot;https://web.dev/discoverable/&quot;&gt;discovered through search&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;why-8&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-8&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One of the biggest advantages of the web is the ability to discover sites and
apps through search. In fact,
&lt;a href=&quot;https://www.brightedge.com/resources/research-reports/channel_share&quot; rel=&quot;noopener&quot;&gt;more than half&lt;/a&gt;
of all website traffic comes from organic search. Making sure that canonical
URLs exist for content and that search engines can index your site is critical
for users to be able to find your PWA. This is especially true when adopting
client-side rendering.&lt;/p&gt;
&lt;h4 id=&quot;how-8&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-8&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Start by ensuring that each URL has a unique, descriptive title and meta
description. Then you can use the
&lt;a href=&quot;https://search.google.com/search-console/about&quot; rel=&quot;noopener&quot;&gt;Google Search Console&lt;/a&gt;
and the &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/seo/&quot; rel=&quot;noopener&quot;&gt;Search Engine Optimization audits&lt;/a&gt; in Lighthouse to
help you debug and fix discoverability issues with your PWA. You can also use
&lt;a href=&quot;https://www.bing.com/toolbox/webmaster&quot; rel=&quot;noopener&quot;&gt;Bing&lt;/a&gt;&#39;s or
&lt;a href=&quot;https://webmaster.yandex.com/welcome/&quot; rel=&quot;noopener&quot;&gt;Yandex&lt;/a&gt;&#39;s webmaster tools, and
consider including &lt;a href=&quot;https://goo.gle/search-gallery&quot; rel=&quot;noopener&quot;&gt;structured data&lt;/a&gt; via
schemas from &lt;a href=&quot;https://schema.org/&quot; rel=&quot;noopener&quot;&gt;Schema.org&lt;/a&gt; in your PWA.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Works with any input type
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Your PWA is equally usable with a mouse, a keyboard, a stylus, or touch.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Your PWA is equally usable with a mouse, a keyboard, a stylus, or touch.&lt;/p&gt;
&lt;h4 id=&quot;why-9&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-9&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Devices offer a variety of input methods, and users should be able to
seamlessly switch between them while using your application. Just as important,
input methods should not depend on screen size, meaning that large viewports
need to support touch and small viewports need to support keyboards and mice.
To the best of your ability, ensure that your application and all of its
features supports usage of any input method your user may choose to use.
Where appropriate, you should also enhance experiences to allow for
input-specific controls as well (such as pull-to-refresh).&lt;/p&gt;
&lt;h4 id=&quot;how-9&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-9&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/blog/pointer-events/&quot; rel=&quot;noopener&quot;&gt;Pointer Events API&lt;/a&gt;
provides a unified interface for working with various input options, and is
especially good for adding stylus support. For supporting both touch and
keyboard, ensure that you&#39;re using the correct semantic elements (anchors,
buttons, form controls, etc.) and not rebuilding these with non-semantic
HTML (which is good for accessibility). When including interactions that
activate on hover, ensure they can activate on click or tap, too.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Provides context for permission requests
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; When asking permission to use powerful APIs, provide context and ask only when the API is needed.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;When asking permission to use powerful APIs, provide context and ask only
when the API is needed.&lt;/p&gt;
&lt;h4 id=&quot;why-10&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-10&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;APIs that trigger a permission prompt, like notifications, geolocation, and
credentials, are intentionally designed to be disruptive to a user because they
tend to be related to powerful functionality that requires opt-in. Triggering
these prompts without additional context, like on page load, makes users
less likely to accept those permissions and more likely to distrust them
in the future. Instead, only trigger those prompts after providing an
in-context rationale to the user for why you need that permission.&lt;/p&gt;
&lt;h4 id=&quot;how-10&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-10&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;a href=&quot;https://web.dev/push-notifications-permissions-ux/&quot;&gt;Permission UX&lt;/a&gt;
article and UX Planet&#39;s
&lt;a href=&quot;https://uxplanet.org/mobile-ux-design-the-right-ways-to-ask-users-for-permissions-6cdd9ab25c27&quot; rel=&quot;noopener&quot;&gt;The Right Ways to Ask Users for Permissions&lt;/a&gt;
are good resources to understand how to design permission prompts that, while
focused on mobile, apply to all PWAs.&lt;/p&gt;
&lt;/details&gt;
&lt;details&gt;
&lt;summary&gt;
  Follows best practices for healthy code
  &lt;p class=&quot;text-base color-core-text gap-top-base&quot;&gt; Keeping your codebase healthy makes it easier to meet your goals and deliver new features.&lt;/p&gt;
&lt;/summary&gt;
&lt;p&gt;Keeping your codebase healthy makes it easier to meet your goals and deliver
new features.&lt;/p&gt;
&lt;h4 id=&quot;why-11&quot;&gt;Why &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#why-11&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There&#39;s a lot that goes into building a modern web application. Keeping your
application up-to-date and your codebase healthy makes it easier for you to
deliver new features that meet the other goals laid out in this checklist.&lt;/p&gt;
&lt;h4 id=&quot;how-11&quot;&gt;How &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-checklist/#how-11&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are a number of high-priority checks to ensure a healthy
codebase: avoiding using libraries with known vulnerabilities, ensuring
you&#39;re not using deprecated APIs, removing web anti-patterns from your
codebase (like using &lt;code&gt;document.write()&lt;/code&gt; or having non-passive scroll event
listeners), and even coding defensively to ensure your PWA doesn&#39;t break
if analytics or other third party libraries fail to load. Consider requiring
static code analysis, like linting, as well as automated testing, in multiple
browsers and release channels. These techniques can help catch errors
before they make it into production.&lt;/p&gt;
&lt;/details&gt;
</content>
    <author>
      <name>Sam Richard</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>What are Progressive Web Apps?</title>
    <link href="https://web.dev/what-are-pwas/"/>
    <updated>2020-01-06T00:00:00Z</updated>
    <id>https://web.dev/what-are-pwas/</id>
    <content type="html" mode="escaped">&lt;p&gt;The web is an incredible platform. Its mix of ubiquity across devices and
operating systems, its user-centered security model, and the fact that neither
its specification nor its implementation are controlled by a single company makes
the web a unique platform to develop software on. Combined with its inherent
linkability, it&#39;s possible to search it and share what you&#39;ve found with
anyone, anywhere. Whenever you go to a website, it&#39;s up-to-date, and your
experience with that site can be as ephemeral or as permanent as you&#39;d like. Web
applications can reach &lt;em&gt;anyone, anywhere, on any device&lt;/em&gt; with a single codebase.&lt;/p&gt;
&lt;p&gt;Platform-specific applications are known for being incredibly rich and reliable. They&#39;re
ever-present, on home screens, docks, and taskbars. They work regardless of
network connection. They launch in their own standalone experience. They can
read and write files from the local file system, access hardware connected via
USB, serial port, or bluetooth, and even interact with data stored on your device,
such as contacts and calendar events. In these applications, you can do things
such as take pictures, see playing songs listed on the home screen, or control song
playback while in another app. Platform-specific applications feel like &lt;em&gt;part&lt;/em&gt; of the
device they run on.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A graph illustrating the relative capabilites and reach of platform-specific apps, with high capabilities, web apps, with high reach, and progressive web apps, which have both high capabilities and high reach.&quot; decoding=&quot;async&quot; height=&quot;367&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/1DKtUFjXLJbiiruKA9P1.svg&quot; width=&quot;370&quot; /&gt;
  &lt;figcaption&gt;
    Capabilities vs. reach of platform-specific apps, web apps, and progressive web apps.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;If you think about platform-specific apps and web apps in terms of capabilities and reach,
platform-specific apps represent the best of capabilities whereas web apps represent the
best of reach. So where do Progressive Web Apps fit in?&lt;/p&gt;
&lt;p&gt;Progressive Web Apps (PWA) are built and enhanced with modern APIs to deliver
enhanced capabilities, reliability, and installability while reaching
&lt;em&gt;anyone, anywhere, on any device&lt;/em&gt; with a single codebase.&lt;/p&gt;
&lt;h2 id=&quot;the-three-app-pillars&quot;&gt;The three app pillars &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-are-pwas/#the-three-app-pillars&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Progressive Web Apps are web applications that have been designed to be
capable, reliable, and installable. These three pillars transform them into an
experience that feels like a platform-specific application.&lt;/p&gt;
&lt;h3 id=&quot;capable&quot;&gt;Capable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-are-pwas/#capable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The web is quite capable in its own right today. For example, you can build a hyper-local
video chat app using WebRTC, geolocation, and push notifications. You can make
that app installable and take its conversations virtual with WebGL and WebVR.
With the introduction of WebAssembly, developers can tap into other ecosystems,
such as C, C++, and Rust, and bring decades of work and capabilities to the web.
&lt;a href=&quot;https://squoosh.app/&quot; rel=&quot;noopener&quot;&gt;Squoosh.app&lt;/a&gt;, for instance, uses this for its
advanced image compression.&lt;/p&gt;
&lt;p&gt;Until recently, only platform-specific apps could really lay claim to these capabilities.
While some capabilities are still out of the web&#39;s reach, new and upcoming APIs
are looking to change that, expanding what the web can do with features like
file system access, media controls, app badging, and full clipboard support. All
of these capabilities are built with the web&#39;s secure, user-centric permission
model, ensuring that going to a website is never scary for users.&lt;/p&gt;
&lt;p&gt;Between modern APIs, WebAssembly, and new and upcoming APIs, web applications
are more capable than ever, and those capabilities are only growing.&lt;/p&gt;
&lt;h3 id=&quot;reliable&quot;&gt;Reliable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-are-pwas/#reliable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A reliable Progressive Web App feels fast and dependable regardless of the
network.&lt;/p&gt;
&lt;p&gt;Speed is critical for getting users to &lt;em&gt;use&lt;/em&gt; your experience. In fact, as page
load times go from 1 second to ten seconds, the probability of a user bouncing
&lt;a href=&quot;https://www.thinkwithgoogle.com/marketing-resources/data-measurement/mobile-page-speed-new-industry-benchmarks/&quot; rel=&quot;noopener&quot;&gt;increases by 123%&lt;/a&gt;.
Performance doesn&#39;t stop after the &lt;code&gt;onload&lt;/code&gt; event. Users should never wonder whether their
interaction—for example, clicking a button—was registered or not. Scrolling and
animation should feel smooth. Performance affects the entire user experience, from
how they perceive your application to how it actually performs.&lt;/p&gt;
&lt;p&gt;Finally, reliable applications need to be usable regardless of network
connection. Users expect apps to start up on slow or flaky network connections
or even when offline. They expect the most recent content they&#39;ve interacted
with, such as media tracks or tickets and itineraries, to be available and usable
even if getting a request to your server is hard. When a request isn&#39;t possible,
they expect to be told there&#39;s trouble instead of silently failing or crashing.&lt;/p&gt;
&lt;p&gt;Users deserve apps that respond to interaction in the blink of an eye, and an
experience they can depend on.&lt;/p&gt;
&lt;h3 id=&quot;installable&quot;&gt;Installable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-are-pwas/#installable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Installed Progressive Web Apps run in a standalone window instead of a browser
tab. They&#39;re launchable from on the user&#39;s home screen, dock, taskbar, or shelf.
It&#39;s possible to search for them on a device and jump between them with the app
switcher, making them feel like part of the device they&#39;re installed on.&lt;/p&gt;
&lt;p&gt;New capabilities open up after a web app is installed. Keyboard shortcuts, usually
reserved when running in the browser, become available. Progressive Web
Apps can register to accept content from other applications, or to be the
default application to handle different types of files.&lt;/p&gt;
&lt;p&gt;When a Progressive Web App moves out of a tab and into a standalone app window,
it transforms how users think about it and interact with it.&lt;/p&gt;
&lt;h2 id=&quot;the-best-of-both-worlds&quot;&gt;The best of both worlds &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/what-are-pwas/#the-best-of-both-worlds&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;At their heart, Progressive Web Apps are just web applications. Using
progressive enhancement, new capabilities are enabled in modern browsers. Using
service workers and a web app manifest, your web application becomes reliable
and installable. If the new capabilities aren&#39;t available, users still get the core experience.&lt;/p&gt;
&lt;p&gt;The numbers don&#39;t lie! Companies that have launched Progressive Web Apps have
seen impressive results. For example, Twitter saw a 65% increase in pages per
session, 75% more Tweets, and a 20% decrease in bounce rate, all while reducing
the size of their app by over 97%. After switching to a PWA, Nikkei saw 2.3
times more organic traffic, 58% more subscriptions, and 49% more daily active
users. Hulu replaced their platform-specific desktop experience with a Progressive Web App
and saw a 27% increase in return visits.&lt;/p&gt;
&lt;p&gt;Progressive Web Apps provide you with a unique opportunity to deliver a web
experience your users will love. Using the latest web features to bring
enhanced capabilities and reliability, Progressive Web Apps allow what you
build to be installed by &lt;em&gt;anyone, anywhere, on any device&lt;/em&gt; with a single
codebase.&lt;/p&gt;
&lt;!--lint disable no-unescaped-template-tags--&gt;
&lt;script type=&quot;speculationRules&quot;&gt;
{
  &quot;prerender&quot;:[
    {
      &quot;source&quot;:&quot;list&quot;,
      &quot;urls&quot;:[
        &quot;/progressive-web-apps/&quot;
      ]
    }
  ]
}
&lt;/script&gt;
&lt;!--lint enable no-unescaped-template-tags--&gt;
</content>
    <author>
      <name>Sam Richard</name>
    </author><author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Address Bar Install for Progressive Web Apps on the Desktop</title>
    <link href="https://web.dev/pwa-install-addressbar/"/>
    <updated>2019-06-12T00:00:00Z</updated>
    <id>https://web.dev/pwa-install-addressbar/</id>
    <content type="html" mode="escaped">&lt;p&gt;On desktop, there&#39;s typically no indication to a user that a Progressive Web
App is installable, and if it is, the install flow is hidden within the three
dot menu.&lt;/p&gt;
&lt;figure class=&quot;float-right&quot;&gt;
  &lt;video autoplay=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/T4FyVKpzu4WKF1kBNvXepbi08t52/Clnl7AHL7JTP8XCSqLTX.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;In Chrome 76 (beta mid-June 2019), we&#39;re making it easier for users to install
Progressive Web Apps on the desktop by adding an install button to the address
bar (omnibox). If a site meets the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#criteria&quot; rel=&quot;noopener&quot;&gt;Progressive Web App installability criteria&lt;/a&gt;,
Chrome will automatically show an install icon in the address bar. Clicking the
button prompts the user to install the PWA.&lt;/p&gt;
&lt;p&gt;Like other install events, you can listen for the &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#appinstalled&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;appinstalled&lt;/code&gt;&lt;/a&gt;
event to detect if the user installed your PWA.&lt;/p&gt;
&lt;h2 id=&quot;adding-your-own-install-prompt&quot;&gt;Adding your own install prompt &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-install-addressbar/#adding-your-own-install-prompt&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If your PWA has use cases where it’s helpful for a user to install your app,
for example if you have users who use your app more than once a week, you
should be promoting the installation of your PWA within the web UI of your app.&lt;/p&gt;
&lt;p&gt;To add your own custom install button, listen for the
&lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/#listen_for_beforeinstallprompt&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;beforeinstallprompt&lt;/code&gt;&lt;/a&gt; event. When it’s fired,
save a reference to the event, and update your user interface to let the user
know they can install your Progressive Web App.&lt;/p&gt;
&lt;h2 id=&quot;patterns-for-promoting-the-installation-of-your-pwa&quot;&gt;Patterns for promoting the installation of your PWA &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/pwa-install-addressbar/#patterns-for-promoting-the-installation-of-your-pwa&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are three key ways you can promote the installation of your PWA:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Automatic browser promotion&lt;/strong&gt;, like the address bar install button.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Application UI promotion&lt;/strong&gt;, where UI elements appear in the application
interface, such as banners, buttons in the header or navigation menu, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inline promotional patterns&lt;/strong&gt; interweave promotions within the site content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Check out Patterns for &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/promoting-install-mobile&quot; rel=&quot;noopener&quot;&gt;Promoting PWA Installation (mobile)&lt;/a&gt;
for more details. Its focus is mobile, but many of the patterns are applicable
for desktop, or can be easily modified to work for desktop experiences.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>How do I notify users that my PWA is installable?</title>
    <link href="https://web.dev/pwa-install-patterns/"/>
    <updated>2019-06-04T00:00:00Z</updated>
    <id>https://web.dev/pwa-install-patterns/</id>
    <content type="html" mode="escaped">&lt;p&gt;If your PWA has use cases where it’s helpful for a user to install your app,
for example if you have users who use your app more than once a week, you
should be promoting the installation of your PWA within the web UI of your app.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;In-feed promo banner screenshot&quot; class=&quot;screenshot&quot; decoding=&quot;async&quot; height=&quot;1000&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/0Nl5R8kgpRnaFI4i1l95.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;strong&gt;But how should you notify the user that your PWA can be installed?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://developers.google.com/web/fundamentals/app-install-banners/promoting-install-mobile&quot; rel=&quot;noopener&quot;&gt;Patterns for Promoting PWA Installation&lt;/a&gt;, a series
of recommended patterns and best practices that you can use to promote the
installation of your Progressive Web App.&lt;/p&gt;
&lt;p&gt;It includes patterns for notifying users within the core UI of your app,
within your app&#39;s content, or just letting the browser notify the user. And,
it includes recommendations on how to place the notification for different
types of apps.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Responsive web design basics</title>
    <link href="https://web.dev/responsive-web-design-basics/"/>
    <updated>2019-02-12T00:00:00Z</updated>
    <id>https://web.dev/responsive-web-design-basics/</id>
    <content type="html" mode="escaped">&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Take a look at our complete course on Responsive Web Design—&lt;a href=&quot;https://web.dev/learn/design/&quot;&gt;Learn Design&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The use of mobile devices to surf the web continues to grow at an astronomical pace, and these devices are often constrained by display size and require a different approach to how content is laid out on the screen.&lt;/p&gt;
&lt;p&gt;Responsive web design, originally defined by &lt;a href=&quot;http://alistapart.com/article/responsive-web-design/&quot; rel=&quot;noopener&quot;&gt;Ethan Marcotte in A List Apart&lt;/a&gt;, responds to the needs of the users and the devices they&#39;re using. The layout changes based on the size and capabilities of the device. For example, on a phone users would see content shown in a single column view; a tablet might show the same content in two columns.&lt;/p&gt;
&lt;figure&gt;
  &lt;video autoplay=&quot;&quot; controls=&quot;&quot; loop=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot;&gt;      &lt;source src=&quot;https://storage.googleapis.com/web-dev-uploads/video/tcFciHGuF3MxnTr1y5ue01OGLBn2/8RKRFvbuoXGkOSuEArb7.mp4&quot; type=&quot;video/mp4&quot; /&gt;    &lt;/video&gt;
  &lt;figcaption&gt;In this video the design moves from a narrow to a wide viewport, responding to the available screen real estate.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A multitude of different screen sizes exist across phones, &amp;quot;phablets,&amp;quot; tablets, desktops, game consoles, TVs, and even wearables. Screen sizes are always changing, so it&#39;s important that your site can adapt to any screen size, today or in the future. In addition, devices have different features with which we interact with them. For example some of your visitors will be using a touchscreen. Modern responsive design considers all of these things to optimize the experience for everyone.&lt;/p&gt;
&lt;h2 id=&quot;viewport&quot;&gt;Set the viewport &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#viewport&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pages optimized for a variety of devices must include a meta viewport tag in the head of the document.
A meta viewport tag gives the browser instructions on how to control the page&#39;s dimensions and scaling.&lt;/p&gt;
&lt;p&gt;To attempt to provide the best experience, mobile browsers render
the page at a desktop screen width (usually about &lt;code&gt;980px&lt;/code&gt;, though this varies
across devices), and then try to make the content look better by increasing
font sizes and scaling the content to fit the screen.
This means that font sizes may appear inconsistent to users,
who may have to double-tap or pinch-to-zoom
in order to see and interact with the content.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    …&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;width=device-width, initial-scale=1&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;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    …&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  …&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Using the meta viewport value &lt;code&gt;width=device-width&lt;/code&gt; instructs the page to match
the screen&#39;s width in device-independent pixels. A device (or density) independent pixel being a representation of a single pixel, which may on a high density screen consist of many physical pixels. This allows the page to reflow
content to match different screen sizes, whether rendered on a small mobile
phone or a large desktop monitor.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a page with the text hard to read as it is very zoomed out.&quot; decoding=&quot;async&quot; height=&quot;640&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/SrMBH5gokGU06S0GsjLS.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
  &lt;figcaption&gt;
    An example of how the page loads in a device without the viewport meta tag. &lt;a href=&quot;https://without-vp-meta.glitch.me/&quot;&gt;See this example on Glitch&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of the same page with the text at a size that can be read.&quot; decoding=&quot;async&quot; height=&quot;888&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 500px) 500px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/9NrJxt3aEv37A3E7km65.png?auto=format&amp;w=1000 1000w&quot; width=&quot;500&quot; /&gt;
  &lt;figcaption&gt;
    An example of how the page loads in a device with the viewport meta tag. &lt;a href=&quot;https://with-vp-meta.glitch.me/&quot;&gt;See this example on Glitch&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;https://css-tricks.com/probably-use-initial-scale1/&quot; rel=&quot;noopener&quot;&gt;Some browsers&lt;/a&gt;
keep the page&#39;s width constant when rotating to landscape
mode, and zoom rather than reflow to fill the screen. Adding the value
&lt;code&gt;initial-scale=1&lt;/code&gt; instructs browsers to establish a 1:1 relationship between CSS
pixels and device-independent pixels regardless of device orientation, and
allows the page to take advantage of the full landscape width.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; To ensure that older browsers can properly parse the attributes, use a comma to separate attributes. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/pwa/viewport/&quot; rel=&quot;noopener&quot;&gt;Does not have a &lt;code&gt;&amp;lt;meta name=&amp;quot;viewport&amp;quot;&amp;gt;&lt;/code&gt; tag with &lt;code&gt;width&lt;/code&gt; or &lt;code&gt;initial-scale&lt;/code&gt;&lt;/a&gt;
Lighthouse audit can help you automate the process of making sure that your HTML documents are using the viewport meta tag correctly.&lt;/p&gt;
&lt;h3 id=&quot;accessible-viewport&quot;&gt;Ensure an accessible viewport &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#accessible-viewport&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In addition to setting an &lt;code&gt;initial-scale&lt;/code&gt;,
you can also set the following attributes on the viewport:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;minimum-scale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;maximum-scale&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user-scalable&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When set, these can disable the user&#39;s ability to zoom the viewport,
potentially causing accessibility issues.
Therefore we would not recommend using these attributes.&lt;/p&gt;
&lt;h2 id=&quot;size-content&quot;&gt;Size content to the viewport &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#size-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;On both desktop and mobile devices,
users are used to scrolling websites vertically but not horizontally;
forcing the user to scroll horizontally or to zoom out
in order to see the whole page results in a poor user experience.&lt;/p&gt;
&lt;p&gt;When developing a mobile site with a meta viewport tag,
it&#39;s easy to accidentally create page content that doesn&#39;t quite fit within the specified viewport.
For example, an image that is displayed at a width wider than the viewport
can cause the viewport to scroll horizontally.
You should adjust this content to fit within the width of the viewport,
so that the user does not need to scroll horizontally.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.chrome.com/docs/lighthouse/pwa/content-width/&quot; rel=&quot;noopener&quot;&gt;Content is not sized correctly for the viewport&lt;/a&gt;
Lighthouse audit can help you automate the process of detecting overflowing content.&lt;/p&gt;
&lt;h3 id=&quot;images&quot;&gt;Images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An image has fixed dimensions and if it is larger than the viewport will cause a scrollbar.
A common way to deal with this problem is to give all images a &lt;code&gt;max-width&lt;/code&gt; of &lt;code&gt;100%&lt;/code&gt;.
This will cause the image to shrink to fit the space it has,
should the viewport size be smaller than the image.
However because the &lt;code&gt;max-width&lt;/code&gt;, rather than the &lt;code&gt;width&lt;/code&gt; is &lt;code&gt;100%&lt;/code&gt;,
the image will not stretch larger than its natural size.
It is generally safe to add the following to your stylesheet
so that you will never have a problem with images causing a scrollbar.&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 selector&quot;&gt;img&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;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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; block&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;h4 id=&quot;image-dimensions&quot;&gt;Add the dimensions of the image to the img element &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#image-dimensions&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When using &lt;code&gt;max-width: 100%&lt;/code&gt; you are overriding the natural dimensions of the image,
however you should still use the &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes on your &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tag.
This is because modern browsers will use this information to reserve space for the image
before it loads in,
this will help to avoid &lt;a href=&quot;https://web.dev/optimize-cls/&quot;&gt;layout shifts&lt;/a&gt; as content loads.&lt;/p&gt;
&lt;h3 id=&quot;layout&quot;&gt;Layout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#layout&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since screen dimensions and width in CSS pixels vary widely between devices
(for example, between phones and tablets, and even between different phones),
content should not rely on a particular viewport width to render well.&lt;/p&gt;
&lt;p&gt;In the past, this required setting elements used to create layout in percentages.
In the example below, you can see a two-column layout with floated elements, sized using pixels.
Once the viewport becomes smaller than the total width of the columns, we have to scroll horizontally
to see the content.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a two-column layout with most of the second column outside the viewport&quot; decoding=&quot;async&quot; height=&quot;504&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/exFCZNQLUveUnpMFjvcj.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    A floated layout using pixels. &lt;a href=&quot;https://layout-floats-px.glitch.me/&quot;&gt;See this example on Glitch&lt;/a&gt;.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;By using percentages for the widths, the columns always remain a certain percentage of the container.
This means that the columns become narrower, rather than creating a scrollbar.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/layout-floats-percent?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=README.md&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;layout-floats-percent on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Modern CSS layout techniques such as Flexbox, Grid Layout, and Multicol
make the creation of these flexible grids much easier.&lt;/p&gt;
&lt;h4 id=&quot;flexbox&quot;&gt;Flexbox &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#flexbox&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;This layout method is ideal when you have a set of items of different sizes
and you would like them to fit comfortably in a row or rows,
with smaller items taking less space and larger ones getting more space.&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 selector&quot;&gt;.items&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;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; space-between&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;In a responsive design, you can use Flexbox to display items as a single row,
or wrapped onto multiple rows as the available space decreases.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 220px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/responsive-flexbox?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;responsive-flexbox on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Flexbox&quot; rel=&quot;noopener&quot;&gt;Read more about Flexbox&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;grid&quot;&gt;CSS Grid Layout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#grid&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;CSS Grid Layout allows for the straightforward creation of flexible grids.
If we consider the earlier floated example,
rather than creating our columns with percentages,
we could use grid layout and the &lt;code&gt;fr&lt;/code&gt; unit,
which represents a portion of the available space in the container.&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 selector&quot;&gt;.container&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;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; grid&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;grid-template-columns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1fr 3fr&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;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/two-column-grid?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;two-column-grid on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;Grid can also be used to create regular grid layouts,
with as many items as will fit.
The number of available tracks will be reduced as the screen size shrinks.
In the below demo, we have as many cards as will fit on each row,
with a minimum size of &lt;code&gt;200px&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/grid-as-many-as-fit?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;grid-as-many-as-fit on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Grids&quot; rel=&quot;noopener&quot;&gt;Read more about CSS Grid Layout&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;multicol&quot;&gt;Multiple-column layout &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#multicol&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;For some types of layout you can use Multiple-column Layout (Multicol),
which can create responsive numbers of columns with the &lt;code&gt;column-width&lt;/code&gt; property.
In the demo below, you can see that columns are added if there is room for another &lt;code&gt;200px&lt;/code&gt; column.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/responsive-multicol?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=style.css&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;responsive-multicol on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/Learn/CSS/CSS_layout/Multiple-column_Layout&quot; rel=&quot;noopener&quot;&gt;Read more about Multicol&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;media-queries&quot;&gt;Use CSS media queries for responsiveness &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#media-queries&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you will need to make more extensive changes to your layout
to support a certain screen size than the techniques shown above will allow.
This is where media queries become useful.&lt;/p&gt;
&lt;p&gt;Media queries are simple filters that can be applied to CSS styles.
They make it easy to change styles based on the types of device rendering the content,
or the features of that device, for example width, height, orientation, ability to hover,
and whether the device is being used as a touchscreen.&lt;/p&gt;
&lt;p&gt;To provide different styles for printing,
you need to target a &lt;em&gt;type&lt;/em&gt; of output so you could include a stylesheet with print styles as follows:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&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;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    …&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;print.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;print&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;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    …&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  …&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Alternatively, you could include print styles within your main stylesheet using a media query:&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;@media&lt;/span&gt; print&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;/* print styles go here */&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;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; It is also possible to include separate stylesheets in your main CSS file using the &lt;code&gt;@import&lt;/code&gt; syntax, &lt;code&gt;@import url(print.css) print;&lt;/code&gt;, however this use is not recommended for performance reasons. See &lt;a href=&quot;https://web.dev/critical-rendering-path-page-speed-rules-and-recommendations/#avoid-css-imports&quot;&gt;Avoid CSS imports&lt;/a&gt; for more details. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;For responsive web design, we are typically querying the &lt;em&gt;features&lt;/em&gt; of the device
in order to provide a different layout for smaller screens,
or when we detect that our visitor is using a touchscreen.&lt;/p&gt;
&lt;h3 id=&quot;viewport-media-queries&quot;&gt;Media queries based on viewport size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#viewport-media-queries&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Media queries enable us to create a responsive experience
where specific styles are applied to small screens, large screens, and anywhere in between.
The feature we are detecting here is therefore screen size,
and we can test for the following things.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;width&lt;/code&gt; (&lt;code&gt;min-width&lt;/code&gt;, &lt;code&gt;max-width&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;height&lt;/code&gt; (&lt;code&gt;min-height&lt;/code&gt;, &lt;code&gt;max-height&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;orientation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aspect-ratio&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/media-queries-size?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;media-queries-size on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;All of these features have excellent browser support,
for more details including browser support information see
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/width&quot; rel=&quot;noopener&quot;&gt;width&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/height&quot; rel=&quot;noopener&quot;&gt;height&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/orientation&quot; rel=&quot;noopener&quot;&gt;orientation&lt;/a&gt;, and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/aspect-ratio&quot; rel=&quot;noopener&quot;&gt;aspect-ratio&lt;/a&gt; on MDN.&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; The specification did include tests for &lt;code&gt;device-width&lt;/code&gt; and &lt;code&gt;device-height&lt;/code&gt;. These have been deprecated and should be avoided. &lt;code&gt;device-width&lt;/code&gt; and &lt;code&gt;device-height&lt;/code&gt; tested for the actual size of the device window which was not useful in practice because this may be different from the viewport the user is looking at, for example if they have resized the browser window. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;capability-media-queries&quot;&gt;Media queries based on device capability &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#capability-media-queries&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Given the range of devices available, we cannot make the assumption that every large device is a regular desktop
or laptop computer, or that people are only using a touchscreen on a small device.
With some newer additions to the media queries specification
we can test for features such as the type of pointer used to interact with the device
and whether the user can hover over elements.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;hover&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pointer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;any-hover&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;any-pointer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try viewing this demo on different devices,
such as a regular desktop computer and a phone or tablet.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/media-query-pointer?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;media-query-pointer on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;These newer features have good support in all modern browsers. Find out more on the MDN pages for
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/hover&quot; rel=&quot;noopener&quot;&gt;hover&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/any-hover&quot; rel=&quot;noopener&quot;&gt;any-hover&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/pointer&quot; rel=&quot;noopener&quot;&gt;pointer&lt;/a&gt;,
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/CSS/@media/any-pointer&quot; rel=&quot;noopener&quot;&gt;any-pointer&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;using-any-hover-and-any-pointer&quot;&gt;Using &lt;code&gt;any-hover&lt;/code&gt; and &lt;code&gt;any-pointer&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#using-any-hover-and-any-pointer&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The features &lt;code&gt;any-hover&lt;/code&gt; and &lt;code&gt;any-pointer&lt;/code&gt; test if the user has the capability
to hover, or use that type of pointer even if it is not the primary way they are interacting with their device.
Be very careful when using these.
Forcing a user to switch to a mouse when they are using their touchscreen is not very friendly!
However, &lt;code&gt;any-hover&lt;/code&gt; and &lt;code&gt;any-pointer&lt;/code&gt; may be useful if it is important to work out what kind of device a user has.
For example, a laptop with a touchscreen and trackpad should match coarse and fine pointers,
in addition to the ability to hover.&lt;/p&gt;
&lt;h2 id=&quot;breakpoints&quot;&gt;How to choose breakpoints &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#breakpoints&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Don&#39;t define breakpoints based on device classes.
Defining breakpoints based on specific devices, products, brand names,
or operating systems that are in use today can result in a maintenance nightmare.
Instead, the content itself should determine how the layout adjusts to its container.&lt;/p&gt;
&lt;h3 id=&quot;major-breakpoints&quot;&gt;Pick major breakpoints by starting small, then working up &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#major-breakpoints&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Design the content to fit on a small screen size first,
then expand the screen until a breakpoint becomes necessary.
This allows you to optimize breakpoints based on content
and maintain the least number of breakpoints possible.&lt;/p&gt;
&lt;p&gt;Let&#39;s work through the example we saw at the beginning: the weather forecast.
The first step is to make the forecast look good on a small screen.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a weather app at a mobile width&quot; decoding=&quot;async&quot; height=&quot;667&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/3KPWtKzDFCwImLyHprRP.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    The app at a narrow width.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Next, resize the browser until there is too much white space between the elements,
and the forecast simply doesn&#39;t look as good.
The decision is somewhat subjective, but above &lt;code&gt;600px&lt;/code&gt; is certainly too wide.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a weather app with wide gaps between items&quot; decoding=&quot;async&quot; height=&quot;240&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/sh1P84rvjvviENlVFED4.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    The app at a point where we feel we should tweak the design.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To insert a breakpoint at &lt;code&gt;600px&lt;/code&gt;, create two media queries at the end of your CSS for the component,
one to use when the browser is &lt;code&gt;600px&lt;/code&gt; and below, and one for when it is wider than &lt;code&gt;600px&lt;/code&gt;.&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 600px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&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 atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 601px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&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;Finally, refactor the CSS. Inside the media query for a &lt;code&gt;max-width&lt;/code&gt; of &lt;code&gt;600px&lt;/code&gt;,
add the CSS which is only for small screens. Inside the media query for a
&lt;code&gt;min-width&lt;/code&gt; of &lt;code&gt;601px&lt;/code&gt; add CSS for larger screens.&lt;/p&gt;
&lt;h4 id=&quot;pick-minor-breakpoints-when-necessary&quot;&gt;Pick minor breakpoints when necessary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#pick-minor-breakpoints-when-necessary&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In addition to choosing major breakpoints when layout changes significantly,
it is also helpful to adjust for minor changes.
For example, between major breakpoints it may be helpful to adjust the margins or padding on an element,
or increase the font size to make it feel more natural in the layout.&lt;/p&gt;
&lt;p&gt;Let&#39;s start by optimizing the small screen layout.
In this case, let&#39;s boost the font when the viewport width is greater than &lt;code&gt;360px&lt;/code&gt;.
Second, when there is enough space,
we can separate the high and low temperatures so that they&#39;re on the same line
instead of on top of each other.
And let&#39;s also make the weather icons a bit larger.&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 360px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;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; 1.0em&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 atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 500px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;.seven-day-fc .temp-low,&lt;br /&gt;  .seven-day-fc .temp-high&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;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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 45%&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 selector&quot;&gt;.seven-day-fc .seven-day-temp&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;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&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 selector&quot;&gt;.seven-day-fc .icon&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 64px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 64px&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;Similarly, for the large screens it&#39;s best to limit to maximum width of the forecast panel
so it doesn&#39;t consume the whole screen width.&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;.weather-forecast&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700px&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;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/responsive-forecast?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=style.css&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;responsive-forecast on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;optimize-text-for-reading&quot;&gt;Optimize text for reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#optimize-text-for-reading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Classic readability theory suggests that an ideal column should contain 70 to 80 characters per line
(about 8 to 10 words in English).
Thus, each time the width of a text block grows past about 10 words,
consider adding a breakpoint.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a a page of text on a mobile device&quot; decoding=&quot;async&quot; height=&quot;488&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/C4IGJw9hbPXKnTSovEXS.jpg?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
  &lt;figcaption&gt;
    The text as read on a mobile device.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of a a page of text on a desktop browser&quot; decoding=&quot;async&quot; height=&quot;377&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/rmsa1EB5FpvWV0vFIpTF.jpg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    The text as read on a desktop browser with a breakpoint added to constrain the line length.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Let&#39;s take a deeper look at the above blog post example.
On smaller screens, the Roboto font at &lt;code&gt;1em&lt;/code&gt; works perfectly giving 10 words per line,
but larger screens require a breakpoint.
In this case, if the browser width is greater than &lt;code&gt;575px&lt;/code&gt;, the ideal content width is &lt;code&gt;550px&lt;/code&gt;.&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 575px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;article&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 550px&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; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&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;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 420px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/rwd-reading?attributionHidden=true&amp;sidebarCollapsed=true&amp;path=index.html&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;rwd-reading on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;avoid-simply-hiding-content&quot;&gt;Avoid simply hiding content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#avoid-simply-hiding-content&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Be careful when choosing what content to hide or show depending on screen size.
Don&#39;t simply hide content just because you can&#39;t fit it on the screen.
Screen size is not a definitive indication of what a user may want.
For example, eliminating the pollen count from the weather forecast
could be a serious issue for spring-time allergy sufferers who need the information
to determine if they can go outside or not.&lt;/p&gt;
&lt;h2 id=&quot;devtools&quot;&gt;View media query breakpoints in Chrome DevTools &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-web-design-basics/#devtools&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you&#39;ve got your media query breakpoints set up,
you&#39;ll want to see how your site looks with them.
You could resize your browser window to trigger the breakpoints,
but Chrome DevTools has a built-in feature that makes it easy to see how a page looks
under different breakpoints.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of DevTools with our weather app open and a width of 822 pixels selected.&quot; decoding=&quot;async&quot; height=&quot;522&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/DhaeCbVo5AmzZ0CyLtVp.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    DevTools showing the weather app as it looks at a wider viewport size.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshot of DevTools with our weather app open and a width of 436 pixels selected.&quot; decoding=&quot;async&quot; height=&quot;521&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/admin/35IEQnhGox93PHvbeglM.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    DevTools showing the weather app as it looks at a narrower viewport size.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;To view your page under different breakpoints:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/open/&quot; rel=&quot;noopener&quot;&gt;Open DevTools&lt;/a&gt;
and then turn on &lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/#toggle&quot; rel=&quot;noopener&quot;&gt;Device Mode&lt;/a&gt;.
This opens in &lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/#responsive&quot; rel=&quot;noopener&quot;&gt;responsive mode&lt;/a&gt; by default.&lt;/p&gt;
&lt;p&gt;To see your media queries, open the Device Mode menu and select
&lt;a href=&quot;https://developer.chrome.com/docs/devtools/device-mode/#queries&quot; rel=&quot;noopener&quot;&gt;Show media queries&lt;/a&gt;
to display your breakpoints as colored bars above your page.&lt;/p&gt;
&lt;p&gt;Click on one of the bars to view your page while that media query is active.
Right-click on a bar to jump to the media query&#39;s definition.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author><author>
      <name>Rachel Andrew</name>
    </author>
  </entry>
  
  <entry>
    <title>Is your app installed? getInstalledRelatedApps() will tell you!</title>
    <link href="https://web.dev/get-installed-related-apps/"/>
    <updated>2018-12-20T00:00:00Z</updated>
    <id>https://web.dev/get-installed-related-apps/</id>
    <content type="html" mode="escaped">&lt;h2 id=&quot;what&quot;&gt;What is the getInstalledRelatedApps() API? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#what&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure data-float=&quot;right&quot;&gt;
  &lt;img alt=&quot;&quot; decoding=&quot;async&quot; height=&quot;486&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 550px) 550px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/vjamv2uyz6NxBPxPIm11.jpg?auto=format&amp;w=1100 1100w&quot; width=&quot;550&quot; /&gt;
  &lt;figcaption&gt;
    A website using &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; to determine if its
    Android app is already installed.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The &lt;a href=&quot;https://wicg.github.io/get-installed-related-apps/spec/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; makes it possible for &lt;em&gt;your&lt;/em&gt; page to
check if &lt;em&gt;your&lt;/em&gt; mobile or desktop app, or in some cases, if your Progressive
Web App (PWA) is already installed on a user&#39;s device, and allows you to
customize the user experience if it is.&lt;/p&gt;
&lt;p&gt;For example, if your app is already installed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redirecting the user from a product marketing page directly into your app.&lt;/li&gt;
&lt;li&gt;Centralizing some functionality like notifications in the other app to
prevent duplicate notifications.&lt;/li&gt;
&lt;li&gt;Not &lt;a href=&quot;https://web.dev/customize-install/&quot;&gt;promoting the installation&lt;/a&gt; of your PWA if your
other app is already installed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;To use the &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; API, you need to tell your app about
your site, then tell your site about your app. Once you&#39;ve defined the
relationship between the two, you can check if the app is installed.&lt;/p&gt;
&lt;h3 id=&quot;supported-app-types-you-can-check&quot;&gt;Supported app types you can check &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#supported-app-types-you-can-check&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div&gt;
  &lt;table data-alignment=&quot;top&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;App type&lt;/th&gt;
        &lt;th&gt;Checkable from&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://web.dev/get-installed-related-apps/#check-android&quot;&gt;Android app&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          Android only&lt;br /&gt;
          Chrome 80 or later
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;
          &lt;a href=&quot;https://web.dev/get-installed-related-apps/#check-windows&quot;&gt;Windows (UWP) app&lt;/a&gt;
        &lt;/td&gt;
        &lt;td&gt;
          Windows only&lt;br /&gt;
          Chrome 85 or later&lt;br /&gt;
          Edge 85 or later
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;
          Progressive Web App&lt;br /&gt;
          Installed in the &lt;a href=&quot;https://web.dev/get-installed-related-apps/#check-pwa-in-scope&quot;&gt;same scope&lt;/a&gt; or a
          &lt;a href=&quot;https://web.dev/get-installed-related-apps/#check-pwa-out-of-scope&quot;&gt;different scope&lt;/a&gt;.
        &lt;/td&gt;
        &lt;td&gt;
          Android only&lt;br /&gt;
          Chrome 84 or later
        &lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; API only allows you to check if &lt;em&gt;your&lt;/em&gt; apps are installed. You cannot get a list of all installed apps, or check if other 3rd party apps are installed. &lt;/div&gt;&lt;/aside&gt;
&lt;!--  Android App --&gt;
&lt;h2 id=&quot;check-android&quot;&gt;Check if your Android app is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-android&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your website can check if your Android app is installed.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Supported on&lt;/p&gt;
&lt;p&gt;Android: Chrome 80 or later&lt;/p&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;tell-your-android-app-about-your-website&quot;&gt;Tell your Android app about your website &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-android-app-about-your-website&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, you&#39;ll need to update your Android app to define the relationship
between your website and Android application using the
&lt;a href=&quot;https://developers.google.com/digital-asset-links/v1/getting-started&quot; rel=&quot;noopener&quot;&gt;Digital Asset Links system&lt;/a&gt;. This ensures that only your
website can check if your Android app is installed.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; of your Android app, add an &lt;code&gt;asset_statements&lt;/code&gt;
entry:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;manifest&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;application&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;meta-data&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;asset_statements&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;resource&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;@string/asset_statements&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;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;application&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;manifest&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;Then, in &lt;code&gt;strings.xml&lt;/code&gt;, add the following asset statement, updating &lt;code&gt;site&lt;/code&gt; with
your domain. Be sure to include the escaping characters.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;string&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;asset_statements&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;br /&gt;    \&quot;relation\&quot;: [\&quot;delegate_permission/common.handle_all_urls\&quot;],&lt;br /&gt;    \&quot;target\&quot;: {&lt;br /&gt;      \&quot;namespace\&quot;: \&quot;web\&quot;,&lt;br /&gt;      \&quot;site\&quot;: \&quot;https://example.com\&quot;&lt;br /&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;string&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;Once completed, publish your updated Android app to the Play store.&lt;/p&gt;
&lt;h3 id=&quot;tell-your-website-about-your-android-app&quot;&gt;Tell your website about your Android app &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-website-about-your-android-app&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, tell your website about your Android app by
&lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;adding a web app manifest&lt;/a&gt; to your page. The manifest must
include the &lt;code&gt;related_applications&lt;/code&gt; property, an array that provides the details
about your app, including &lt;code&gt;platform&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;platform&lt;/code&gt; must be &lt;code&gt;play&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; is the GooglePlay application ID for your Android app&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;related_applications&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;platform&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;play&quot;&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;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;com.android.chrome&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;check-if-your-app-is-installed&quot;&gt;Check if your app is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-if-your-app-is-installed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, call &lt;a href=&quot;https://web.dev/get-installed-related-apps/#use&quot;&gt;&lt;code&gt;navigator.getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; to check if your
Android app is installed.&lt;/p&gt;
&lt;p&gt;Try the &lt;a href=&quot;https://get-installed-apps.glitch.me/&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt;&lt;/p&gt;
&lt;!--  Windows App --&gt;
&lt;h2 id=&quot;check-windows&quot;&gt;Check if your Windows (UWP) app is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-windows&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your website can check if your Windows app (built using UWP) is installed.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Supported on&lt;/p&gt;
&lt;p&gt;Windows: Chrome 85 or later, Edge 85 or later&lt;/p&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;tell-your-windows-app-about-your-website&quot;&gt;Tell your Windows app about your website &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-windows-app-about-your-website&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You&#39;ll need to update your Windows app to define the relationship between your
website and Windows application using &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/launch-resume/web-to-app-linking&quot; rel=&quot;noopener&quot;&gt;URI Handlers&lt;/a&gt;. This
ensures that only your website can check if your Windows app is installed.&lt;/p&gt;
&lt;p&gt;Add the &lt;code&gt;Windows.appUriHandler&lt;/code&gt; extension registration to your app&#39;s manifest
file &lt;code&gt;Package.appxmanifest&lt;/code&gt;. For example, if your website&#39;s address is
&lt;code&gt;example.com&lt;/code&gt; you would add the following entry in your app&#39;s manifest:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;Applications&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;Application&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;App&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;...&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;Extensions&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;&lt;span class=&quot;token namespace&quot;&gt;uap3:&lt;/span&gt;Extension&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;Category&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;windows.appUriHandler&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;&lt;span class=&quot;token namespace&quot;&gt;uap3:&lt;/span&gt;AppUriHandler&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;&lt;span class=&quot;token namespace&quot;&gt;uap3:&lt;/span&gt;Host&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;Name&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;example.com&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;&lt;span class=&quot;token namespace&quot;&gt;uap3:&lt;/span&gt;AppUriHandler&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;&lt;span class=&quot;token namespace&quot;&gt;uap3:&lt;/span&gt;Extension&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;Extensions&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;Application&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;Applications&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;Note, you may need to add the &lt;a href=&quot;https://docs.microsoft.com/en-us/uwp/schemas/appxpackage/uapmanifestschema/element-uap3-extension-manual#examples&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;uap3&lt;/code&gt; namespace&lt;/a&gt; to your
&lt;code&gt;&amp;lt;Package&amp;gt;&lt;/code&gt; attribute.&lt;/p&gt;
&lt;p&gt;Then, create a JSON file (without the &lt;code&gt;.json&lt;/code&gt; file extension) named
&lt;code&gt;windows-app-web-link&lt;/code&gt; and provide your app&#39;s package family name. Place
that file either on your server root, or in the &lt;code&gt;/.well-known/&lt;/code&gt; directory. You
can find the package family name in the Packaging section in the app manifest
designer.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;packageFamilyName&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyApp_9jmtgj1pbbz6e&quot;&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;&quot;paths&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;*&quot;&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;See &lt;a href=&quot;https://docs.microsoft.com/en-us/windows/uwp/launch-resume/web-to-app-linking&quot; rel=&quot;noopener&quot;&gt;Enable apps for websites using app URI handlers&lt;/a&gt; for
complete details on setting up URI handlers.&lt;/p&gt;
&lt;h3 id=&quot;tell-your-website-about-your-windows-app&quot;&gt;Tell your website about your Windows app &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-website-about-your-windows-app&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, tell your website about your Windows app by
&lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;adding a web app manifest&lt;/a&gt; to your page. The manifest must
include &lt;code&gt;related_applications&lt;/code&gt; property, an array that provides the details
about your app, including &lt;code&gt;platform&lt;/code&gt; and &lt;code&gt;id&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;platform&lt;/code&gt; must be &lt;code&gt;windows&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; is your app&#39;s package family name, appended by the &lt;code&gt;&amp;lt;Application&amp;gt;&lt;/code&gt; &lt;code&gt;Id&lt;/code&gt;
value in your &lt;code&gt;Package.appxmanifest&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;related_applications&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;platform&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;windows&quot;&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;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MyApp_9jmtgj1pbbz6e!App&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;check-if-your-app-is-installed-2&quot;&gt;Check if your app is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-if-your-app-is-installed-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, call &lt;a href=&quot;https://web.dev/get-installed-related-apps/#use&quot;&gt;&lt;code&gt;navigator.getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; to check if your
Windows app is installed.&lt;/p&gt;
&lt;!--  PWA - in scope --&gt;
&lt;h2 id=&quot;check-pwa-in-scope&quot;&gt;Check if your Progressive Web App is already installed (in scope) &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-pwa-in-scope&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your PWA can check to see if it is already installed. In this case, the page
making the request must be on the same domain, and within the &lt;a href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;scope&lt;/a&gt;
of your PWA, as defined by the scope in the web app manifest.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Supported on&lt;/p&gt;
&lt;p&gt;Android: Chrome 84 or later&lt;/p&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;tell-your-pwa-about-itself&quot;&gt;Tell your PWA about itself &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-pwa-about-itself&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tell your PWA about itself by adding a &lt;code&gt;related_applications&lt;/code&gt; entry in your
PWAs &lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;web app manifest&lt;/a&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;platform&lt;/code&gt; must be &lt;code&gt;webapp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt; is the full path to the web app manifest of your PWA&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  …&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&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;&quot;start_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&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;&quot;related_applications&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;platform&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webapp&quot;&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;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://example.com/manifest.json&quot;&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;br /&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;h3 id=&quot;check-if-your-pwa-is-installed&quot;&gt;Check if your PWA is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-if-your-pwa-is-installed&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, call &lt;a href=&quot;https://web.dev/get-installed-related-apps/#use&quot;&gt;&lt;code&gt;navigator.getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; from within the
&lt;a href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;scope&lt;/a&gt; of your PWA to check if it is installed. If
&lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; is called outside the scope of your PWA, it will
return false. See the next section for details.&lt;/p&gt;
&lt;p&gt;Try the &lt;a href=&quot;https://gira-same-domain.glitch.me/pwa/&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt;&lt;/p&gt;
&lt;!--  PWA - NOT in scope --&gt;
&lt;h2 id=&quot;check-pwa-out-of-scope&quot;&gt;Check if your Progressive Web App is installed (out of scope)  &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-pwa-out-of-scope&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Your website can check if your PWA is installed, even if the page is outside
the &lt;a href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;scope&lt;/a&gt; of your PWA. For example, a landing page served from
&lt;code&gt;/landing/&lt;/code&gt; can check if the PWA served from &lt;code&gt;/pwa/&lt;/code&gt; is installed, or if your
landing page is served from &lt;code&gt;www.example.com&lt;/code&gt; and your PWA is served from
&lt;code&gt;app.example.com&lt;/code&gt;.&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Supported on&lt;/p&gt;
&lt;p&gt;Android: Chrome 84 or later&lt;/p&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;tell-your-pwa-about-your-website&quot;&gt;Tell your PWA about your website &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-pwa-about-your-website&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, you&#39;ll need to add digital asset links to the server where your PWA is
served from. This will help define the relationship between your website and
your PWA, and ensures that only your website can check if your PWA is installed.&lt;/p&gt;
&lt;p&gt;Add an &lt;a href=&quot;https://developers.google.com/digital-asset-links/v1/getting-started&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;assetlinks.json&lt;/code&gt;&lt;/a&gt;
file to the &lt;a href=&quot;https://tools.ietf.org/html/rfc5785&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;/.well-known/&lt;/code&gt;&lt;/a&gt; directory
of the domain where the PWA lives, for example &lt;code&gt;app.example.com&lt;/code&gt;. In the &lt;code&gt;site&lt;/code&gt;
property, provide the full path to the web app manifest that will perform
the check (not the web app manifest of your PWA).&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Served from https://app.example.com/.well-known/assetlinks.json&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;span class=&quot;token property&quot;&gt;&quot;relation&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;delegate_permission/common.query_webapk&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;namespace&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;web&quot;&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;&quot;site&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://www.example.com/manifest.json&quot;&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;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Double check the file name when you create your &lt;code&gt;assetlinks.json&lt;/code&gt; file, I&#39;ve wasted many hours debugging, only to realize I&#39;d added an extra &#39;s&#39; in the file name. &lt;/div&gt;&lt;/aside&gt;
&lt;h3 id=&quot;tell-your-website-about-your-pwa&quot;&gt;Tell your website about your PWA &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#tell-your-website-about-your-pwa&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, tell your website about your PWA app by
&lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;adding a web app manifest&lt;/a&gt; to your page. The manifest must
include the &lt;code&gt;related_applications&lt;/code&gt; property, an array that provides the details
about your PWA, including &lt;code&gt;platform&lt;/code&gt; and &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;platform&lt;/code&gt; must be &lt;code&gt;webapp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;url&lt;/code&gt; is the full path to the web app manifest of your PWA&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;related_applications&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;platform&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;webapp&quot;&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;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://app.example.com/manifest.json&quot;&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;check-if-your-pwa-is-installed-2&quot;&gt;Check if your PWA is installed &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#check-if-your-pwa-is-installed-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, call &lt;a href=&quot;https://web.dev/get-installed-related-apps/#use&quot;&gt;&lt;code&gt;navigator.getInstalledRelatedApps()&lt;/code&gt;&lt;/a&gt; to check if your
PWA is installed.&lt;/p&gt;
&lt;p&gt;Try the &lt;a href=&quot;https://gira-same-domain.glitch.me/&quot; rel=&quot;noopener&quot;&gt;demo&lt;/a&gt;&lt;/p&gt;
&lt;!--  Use the API--&gt;
&lt;h2 id=&quot;use&quot;&gt;Calling getInstalledRelatedApps() &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#use&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Calling &lt;code&gt;navigator.getInstalledRelatedApps()&lt;/code&gt; returns a promise that
resolves with an array of your apps that are installed on the user&#39;s device.&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; relatedApps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; navigator&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getInstalledRelatedApps&lt;/span&gt;&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;relatedApps&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;app&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;platform&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&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;To prevent sites from testing an overly broad set of their own apps,
only the first three apps declared in the web app manifest will be
taken into account.&lt;/p&gt;
&lt;p&gt;Like most other powerful web APIs, the &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; API is
only available when served over &lt;strong&gt;HTTPS&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;questions&quot;&gt;Still have questions? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#questions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Still have questions? Check the &lt;a href=&quot;https://stackoverflow.com/search?q=getinstalledrelatedapps&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;getInstalledRelatedApps&lt;/code&gt; tag on StackOverflow&lt;/a&gt;
to see if anyone else has had similar questions. If not, ask your
&lt;a href=&quot;https://stackoverflow.com/questions/tagged/progressive-web-apps&quot; rel=&quot;noopener&quot;&gt;question&lt;/a&gt; there, and be sure to tag it with the
&lt;a href=&quot;https://stackoverflow.com/questions/tagged/progressive-web-apps&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;progressive-web-apps&lt;/code&gt;&lt;/a&gt; tag. Our team frequently monitors
that tag and tries to answer your questions.&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Did you find a bug with Chrome&#39;s implementation? Or is the implementation
different from the spec?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;File a bug at &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/entry?components=Mobile%3EWebAPKs&quot; rel=&quot;noopener&quot;&gt;https://new.crbug.com&lt;/a&gt;. Include as much
detail as you can, provide simple instructions for reproducing the bug, and
enter &lt;code&gt;Mobile&amp;gt;WebAPKs&lt;/code&gt; in the &lt;strong&gt;Components&lt;/strong&gt; box. &lt;a href=&quot;https://glitch.com/&quot; rel=&quot;noopener&quot;&gt;Glitch&lt;/a&gt;
works great for sharing quick and easy repros.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;show-support-for-the-api&quot;&gt;Show support for the API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#show-support-for-the-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Are you planning to use the &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; API? Your public
support helps the Chrome team to prioritize features and shows other
browser vendors how critical it is to support them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Share how you plan to use the API on the &lt;a href=&quot;https://discourse.wicg.io/t/proposal-get-installed-related-apps-api/1602&quot; rel=&quot;noopener&quot;&gt;WICG Discourse thread&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Send a tweet to &lt;a href=&quot;https://twitter.com/chromiumdev&quot; rel=&quot;noopener&quot;&gt;@ChromiumDev&lt;/a&gt; using the hashtag
&lt;a href=&quot;https://twitter.com/search?q=%23getInstalledRelatedApps&amp;amp;src=typed_query&amp;amp;f=live&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;#getInstalledRelatedApps&lt;/code&gt;&lt;/a&gt;
and let us know where and how you&#39;re using it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;helpful&quot;&gt;Helpful links &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#helpful&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/WICG/get-installed-related-apps/blob/main/EXPLAINER.md&quot; rel=&quot;noopener&quot;&gt;Public explainer for &lt;code&gt;getInstalledRelatedApps()&lt;/code&gt; API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wicg.github.io/get-installed-related-apps/spec/&quot; rel=&quot;noopener&quot;&gt;Spec draft&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=895854&quot; rel=&quot;noopener&quot;&gt;Tracking bug&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.chromestatus.com/feature/5695378309513216&quot; rel=&quot;noopener&quot;&gt;ChromeStatus.com entry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Blink Component: &lt;a href=&quot;https://chromestatus.com/features#component%3A%20Mobile%3EWebAPKs&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Mobile&amp;gt;WebAPKs&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;thanks&quot;&gt;Thanks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/get-installed-related-apps/#thanks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Special thanks to Sunggook Chue at Microsoft for helping with the details
for testing Windows apps, and Rayan Kanso for help with the Chrome details.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Make it installable</title>
    <link href="https://web.dev/codelab-make-installable/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/codelab-make-installable/</id>
    <content type="html" mode="escaped">&lt;p&gt;This glitch contains the &lt;a href=&quot;https://glitch.com/edit/#!/make-it-installable?path=manifest.json&quot; rel=&quot;noopener&quot;&gt;web manifest&lt;/a&gt; with the required fields to make a Web App &lt;a href=&quot;https://web.dev/install-criteria/&quot;&gt;installable&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It also has an install button that is hidden by default.&lt;/p&gt;
&lt;h2 id=&quot;listen-for-the-beforeinstallprompt-event&quot;&gt;Listen for the beforeinstallprompt event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-make-installable/#listen-for-the-beforeinstallprompt-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When the browser fires the &lt;code&gt;beforeinstallprompt&lt;/code&gt; event, that&#39;s the indication
that the Web App can be installed and an install button can be shown
to the user. The &lt;code&gt;beforeinstallprompt&lt;/code&gt; event is fired when the app meets &lt;a href=&quot;https://web.dev/install-criteria/&quot;&gt;the
installability criteria&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Capturing the event enables developers to customize the installation and prompt the user
to install when they consider it is the best time.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click &lt;strong&gt;Remix to Edit&lt;/strong&gt; to make the project editable.&lt;/li&gt;
&lt;li&gt;Add a &lt;code&gt;beforeinstallprompt&lt;/code&gt; event handler to the &lt;code&gt;window&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Save the &lt;code&gt;event&lt;/code&gt; as a global variable; we&#39;ll need it later to show the
prompt.&lt;/li&gt;
&lt;li&gt;Unhide the install button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;beforeinstallprompt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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 comment&quot;&gt;// Prevent the mini-infobar from appearing on mobile.&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;👍&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;beforeinstallprompt&#39;&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Stash the event so it can be triggered later.&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deferredPrompt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Remove the &#39;hidden&#39; class from the install button container.&lt;/span&gt;&lt;br /&gt;  divInstall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token 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;&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;h2 id=&quot;handle-the-install-button-click&quot;&gt;Handle the install button click &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-make-installable/#handle-the-install-button-click&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To show the install prompt, call &lt;code&gt;prompt()&lt;/code&gt; on the saved &lt;code&gt;beforeinstallprompt&lt;/code&gt;
event. Calling &lt;code&gt;prompt()&lt;/code&gt; is done in the install button click handler because
&lt;code&gt;prompt()&lt;/code&gt; must be called from a user gesture.&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; You need to open your app in its own browser tab. The side-by-side preview pane in Glitch doesn&#39;t work for this codelab, since it will be run in an iframe. Choose Glitch&#39;s &amp;quot;Preview in a new window&amp;quot; mode instead. &lt;/div&gt;&lt;/aside&gt;
&lt;ol&gt;
&lt;li&gt;Add a click event handler for the install button.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;prompt()&lt;/code&gt; on the saved &lt;code&gt;beforeinstallprompt&lt;/code&gt; event.&lt;/li&gt;
&lt;li&gt;Log the results of the prompt.&lt;/li&gt;
&lt;li&gt;Set the saved &lt;code&gt;beforeinstallprompt&lt;/code&gt; event to null.&lt;/li&gt;
&lt;li&gt;Hide the install button.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;butInstall&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;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;👍&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;butInstall-clicked&#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;const&lt;/span&gt; promptEvent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deferredPrompt&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;promptEvent&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;// The deferred prompt isn&#39;t available.&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 comment&quot;&gt;// Show the install prompt.&lt;/span&gt;&lt;br /&gt;  promptEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prompt&lt;/span&gt;&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 comment&quot;&gt;// Log the result&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; promptEvent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;userChoice&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;log&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 string&quot;&gt;&#39;userChoice&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result&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;// Reset the deferred prompt variable, since&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// prompt() can only be called once.&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deferredPrompt &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 comment&quot;&gt;// Hide the install button.&lt;/span&gt;&lt;br /&gt;  divInstall&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;classList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toggle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;hidden&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token 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;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;h2 id=&quot;track-the-install-event&quot;&gt;Track the install event &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-make-installable/#track-the-install-event&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Installing a Web App through an install button is only one way users
can install it. They can also use Chrome&#39;s menu, the mini-infobar, and
through &lt;a href=&quot;https://web.dev/promote-install/#browser-promotion&quot;&gt;an icon in the omnibox&lt;/a&gt;. You can
track all of these ways of installation by listening for the &lt;code&gt;appinstalled&lt;/code&gt;
event.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add an &lt;code&gt;appinstalled&lt;/code&gt; event handler to the &lt;code&gt;window&lt;/code&gt; object.&lt;/li&gt;
&lt;li&gt;Log the install event to analytics or other mechanism.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Code:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;appinstalled&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;👍&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;appinstalled&#39;&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;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token comment&quot;&gt;// Clear the deferredPrompt so it can be garbage collected&lt;/span&gt;&lt;br /&gt;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;deferredPrompt &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;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;h2 id=&quot;further-reading&quot;&gt;Further reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/codelab-make-installable/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Congratulations, you app is now installable!&lt;/p&gt;
&lt;p&gt;Here are some additional things that you can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/customize-install/#detect-mode&quot;&gt;Detect if your app is launched from the home screen&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.chrome.com/blog/app-install-banners-native/&quot; rel=&quot;noopener&quot;&gt;Show the operating system&#39;s app install prompt instead&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Add a web app manifest</title>
    <link href="https://web.dev/add-manifest/"/>
    <updated>2018-11-05T00:00:00Z</updated>
    <id>https://web.dev/add-manifest/</id>
    <content type="html" mode="escaped">&lt;p&gt;The web app manifest is a JSON file that tells the browser about your
Progressive Web App and how it should behave when installed on the user&#39;s
desktop or mobile device. A typical manifest file includes the app name, the
icons the app should use, and the URL that should be opened when the
app is launched, among other things.&lt;/p&gt;
&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 39, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      39
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox, Not supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Edge 79, Supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
79
&lt;/span&gt;
&lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
&lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
&lt;span class=&quot;visually-hidden&quot;&gt;Safari, Not supported&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;no&quot; title=&quot;Not supported&quot; aria-label=&quot;Not supported&quot;&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;/span&gt;
&lt;/li&gt;&lt;p&gt;&lt;/p&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/Manifest/name#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;h2 id=&quot;create&quot;&gt;Create the manifest file &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#create&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The manifest file can have any name, but is commonly named &lt;code&gt;manifest.json&lt;/code&gt; and
served from the root (your website&#39;s top-level directory). The specification
suggests the extension should be &lt;code&gt;.webmanifest&lt;/code&gt;, but browsers also support
&lt;code&gt;.json&lt;/code&gt; extensions, which may be easier for developers to understand.&lt;/p&gt;
&lt;p&gt;A typical manifest looks something like this:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;short_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Weather&quot;&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;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Weather: Do I need an umbrella?&quot;&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;&quot;icons&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/icons-vector.svg&quot;&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/svg+xml&quot;&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;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;512x512&quot;&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;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/icons-192.png&quot;&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/png&quot;&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;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192x192&quot;&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;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/icons-512.png&quot;&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/png&quot;&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;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;512x512&quot;&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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/?source=pwa&quot;&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;&quot;start_url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/?source=pwa&quot;&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;&quot;background_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#3367D6&quot;&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;&quot;display&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;standalone&quot;&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;&quot;scope&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&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;&quot;theme_color&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#3367D6&quot;&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;&quot;shortcuts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;How&#39;s weather today?&quot;&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;&quot;short_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Today&quot;&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;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;View weather information for today&quot;&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;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/today?source=pwa&quot;&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;&quot;icons&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/today.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192x192&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;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;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;How&#39;s weather tomorrow?&quot;&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;&quot;short_name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tomorrow&quot;&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;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;View weather information for tomorrow&quot;&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;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/tomorrow?source=pwa&quot;&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;&quot;icons&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/tomorrow.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;192x192&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;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;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Weather forecast information&quot;&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;&quot;screenshots&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/screenshot1.png&quot;&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/png&quot;&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;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;540x720&quot;&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;&quot;form_factor&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;narrow&quot;&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;br /&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/images/screenshot2.jpg&quot;&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;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;image/jpg&quot;&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;&quot;sizes&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;720x540&quot;&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;&quot;form_factor&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wide&quot;&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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;manifest-properties&quot;&gt;Key manifest properties &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#manifest-properties&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;name&quot;&gt;&lt;code&gt;short_name&lt;/code&gt; and/or &lt;code&gt;name&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#name&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You must provide at least the &lt;code&gt;short_name&lt;/code&gt; or &lt;code&gt;name&lt;/code&gt; property. If both are
provided, &lt;code&gt;short_name&lt;/code&gt; is used on the user&#39;s home screen, launcher, or other
places where space may be limited. &lt;code&gt;name&lt;/code&gt; is used when the app is installed.&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; Operating systems usually expect to have a title for each app window. This title is displayed in various window-switching surfaces such as &lt;kbd&gt;alt&lt;/kbd&gt;+&lt;kbd&gt;tab&lt;/kbd&gt;, overview mode, and the shelf window list.  For PWAs running in standalone mode, Chromium prepends the &lt;code&gt;short_name&lt;/code&gt; (or, if it&#39;s not available, the &lt;code&gt;name&lt;/code&gt;) to what is specified in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; of the HTML document to prevent disguise attacks where standalone apps might try to be mistaken, for example, for operating system dialogs.  In consequence, developers should &lt;em&gt;not&lt;/em&gt; repeat the application name in the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; when the app is running in standalone mode. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;icons&quot;&gt;&lt;code&gt;icons&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#icons&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When a user installs your PWA, you can define a set of icons for the browser
to use on the home screen, app launcher, task switcher, splash screen, and so on.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;icons&lt;/code&gt; property is an array of image objects. Each object must
include the &lt;code&gt;src&lt;/code&gt;, a &lt;code&gt;sizes&lt;/code&gt; property, and the &lt;code&gt;type&lt;/code&gt; of image. To use
&lt;a href=&quot;https://web.dev/maskable-icon/&quot;&gt;maskable icons&lt;/a&gt;, sometimes referred to as adaptive
icons on Android, you&#39;ll also need to add &lt;code&gt;&amp;quot;purpose&amp;quot;: &amp;quot;any maskable&amp;quot;&lt;/code&gt; to the
&lt;code&gt;icon&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;For Chromium, you must provide at least a 192x192 pixel icon, and a 512x512
pixel icon. If only those two icon sizes are provided, Chrome
automatically scales the icons to fit the device. If you&#39;d prefer to scale your
own icons, and adjust them for pixel-perfection, provide icons in increments
of 48dp.&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; Chromium-based browsers also support SVG icons which can be scaled arbitrarily without looking pixelated and that support advanced features like &lt;a href=&quot;https://blog.tomayac.com/2021/07/21/dark-mode-web-app-manifest-app-icons/&quot;&gt;responsiveness to &lt;code&gt;prefers-color-scheme&lt;/code&gt;&lt;/a&gt;, with the caveat that the icons do not update live, but remain in the state they were in at install time.  To use SVG icons safely, you should always specify a rasterized icon as a fallback for browsers that do not support SVG icons. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;id&quot;&gt;&lt;code&gt;id&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#id&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; property allows you to explicitly define the identifier used for your application. Adding the &lt;code&gt;id&lt;/code&gt; property to the manifest removes the dependency on the &lt;code&gt;start_url&lt;/code&gt; or the location of the manifest, and makes it possible for them to be updated in the future. For more information, see &lt;a href=&quot;https://developer.chrome.com/blog/pwa-manifest-id/&quot; rel=&quot;noopener&quot;&gt;Uniquely identifying PWAs with the web app manifest id property&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;start-url&quot;&gt;&lt;code&gt;start_url&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#start-url&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;start_url&lt;/code&gt; is required and tells the browser where your application
should start when it is launched, and prevents the app from starting on
whatever page the user was on when they added your app to their home screen.&lt;/p&gt;
&lt;p&gt;Your &lt;code&gt;start_url&lt;/code&gt; should direct the user straight into your app, rather than
a product landing page. Think about what the user will want to do once
they open your app, and place them there.&lt;/p&gt;
&lt;h4 id=&quot;background-color&quot;&gt;&lt;code&gt;background_color&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#background-color&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;background_color&lt;/code&gt; property is used on the splash screen when the
application is first launched on mobile.&lt;/p&gt;
&lt;h4 id=&quot;display&quot;&gt;&lt;code&gt;display&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#display&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;You can customize what browser UI is shown when your app is launched. For
example, you can hide the address bar and browser
user interface elements. Games can even
be made to launch full screen. The &lt;code&gt;display&lt;/code&gt; property takes one of the following values:&lt;/p&gt;
&lt;div class=&quot;table-wrapper&quot;&gt;
  &lt;table id=&quot;display-params&quot;&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;&lt;strong&gt;Property&lt;/strong&gt;&lt;/th&gt;
        &lt;th&gt;&lt;strong&gt;Use&lt;/strong&gt;&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;fullscreen&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;
          Opens the web application without any browser UI and takes
          up the entirety of the available display area.
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;standalone&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;
          Opens the web app to look and feel like a standalone
          app. The app runs in its own window, separate from the browser, and
          hides standard browser UI elements such as the URL bar.
          &lt;figure&gt;
            &lt;img alt=&quot;An example of a PWA window with standalone display.&quot; decoding=&quot;async&quot; height=&quot;196&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/XdBsDeRZozIyXyiXA59n.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
          &lt;/figure&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;minimal-ui&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;
          This mode is similar to &lt;code&gt;standalone&lt;/code&gt;, but provides the
          user a minimal set of UI elements for controlling navigation (such
          as back and reload).
          &lt;figure&gt;
            &lt;img alt=&quot;An example of a PWA window with minimal-ui display.&quot; decoding=&quot;async&quot; height=&quot;196&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/trPwjcMio7tBKGBNoT9u.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
          &lt;/figure&gt;
        &lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;&lt;code&gt;browser&lt;/code&gt;&lt;/td&gt;
        &lt;td&gt;A standard browser experience.&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/div&gt;
&lt;h4 id=&quot;display-override&quot;&gt;&lt;code&gt;display_override&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#display-override&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Web apps can choose how they are displayed by setting a &lt;code&gt;display&lt;/code&gt; mode in their manifest as
&lt;a href=&quot;https://web.dev/add-manifest/#display&quot;&gt;explained above&lt;/a&gt;. Browsers are &lt;em&gt;not&lt;/em&gt; required to support all display modes, but they
&lt;em&gt;are&lt;/em&gt; required to support the
&lt;a href=&quot;https://w3c.github.io/manifest/#dfn-fallback-display-mode&quot; rel=&quot;noopener&quot;&gt;spec-defined fallback chain&lt;/a&gt;
(&lt;code&gt;&amp;quot;fullscreen&amp;quot;&lt;/code&gt; → &lt;code&gt;&amp;quot;standalone&amp;quot;&lt;/code&gt; → &lt;code&gt;&amp;quot;minimal-ui&amp;quot;&lt;/code&gt; → &lt;code&gt;&amp;quot;browser&amp;quot;&lt;/code&gt;). If they don&#39;t support a given
mode, they fall back to the next display mode in the chain. This inflexible behavior can be
problematic in rare cases. For example, a developer cannot request &lt;code&gt;&amp;quot;minimal-ui&amp;quot;&lt;/code&gt; without being
forced back into the &lt;code&gt;&amp;quot;browser&amp;quot;&lt;/code&gt; display mode when &lt;code&gt;&amp;quot;minimal-ui&amp;quot;&lt;/code&gt; is not supported.
Another problem is that the current behavior makes it impossible to introduce new display
modes in a backward compatible way, since explorations like tabbed application mode don&#39;t have a
natural place in the fallback chain.&lt;/p&gt;
&lt;p&gt;These problems are solved by the &lt;code&gt;display_override&lt;/code&gt; property, which the browser considers &lt;em&gt;before&lt;/em&gt;
the &lt;code&gt;display&lt;/code&gt; property. Its value is a sequence of strings that are considered in the listed order, and the
first supported display mode is applied. If none are supported, the browser falls back to evaluating
the &lt;code&gt;display&lt;/code&gt; field.&lt;/p&gt;
&lt;p&gt;Consider the example below. (The details of
&lt;a href=&quot;https://web.dev/window-controls-overlay/&quot;&gt;&lt;code&gt;&amp;quot;window-control-overlay&amp;quot;&lt;/code&gt;&lt;/a&gt; are out-of-scope for
this article.)&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;display_override&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;window-control-overlay&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;minimal-ui&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;display&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;standalone&quot;&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;As stated, the browser will look at &lt;code&gt;display_override&lt;/code&gt; first.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;window-control-overlay&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;minimal-ui&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If neither option is available, it falls back to &lt;code&gt;display&lt;/code&gt;. If &lt;code&gt;&amp;quot;standalone&amp;quot;&lt;/code&gt; is
not available, it resumes spec-defined fallabck chain from that point.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;standalone&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;minimal-ui&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;browser&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; The browser will not consider &lt;code&gt;display_override&lt;/code&gt; unless &lt;code&gt;display&lt;/code&gt; is also present. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;scope&quot;&gt;&lt;code&gt;scope&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;scope&lt;/code&gt; defines the set of URLs that the browser considers to be within your
app, and is used to decide when the user has left the app. The &lt;code&gt;scope&lt;/code&gt;
controls the URL structure that encompasses all the entry and exit points in
your web app. Your &lt;code&gt;start_url&lt;/code&gt; must reside within the &lt;code&gt;scope&lt;/code&gt;.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; If the user clicks a link in your app that navigates outside of the &lt;code&gt;scope&lt;/code&gt;, the link opens and renders within the existing PWA window. If you want the link to open in a browser tab, you must add &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; to the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; tag. On Android, links with &lt;code&gt;target=&amp;quot;_blank&amp;quot;&lt;/code&gt; open in a &lt;a href=&quot;https://developer.chrome.com/multidevice/android/customtabs&quot;&gt;Chrome Custom Tab&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;A few other notes on &lt;code&gt;scope&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If you don&#39;t include a &lt;code&gt;scope&lt;/code&gt; in your manifest, then the default implied
&lt;code&gt;scope&lt;/code&gt; is the directory that your web app manifest is served from.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;scope&lt;/code&gt; attribute can be a relative path (&lt;code&gt;../&lt;/code&gt;), or any higher level
path (&lt;code&gt;/&lt;/code&gt;) which would allow for an increase in coverage of navigations
in your web app.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;start_url&lt;/code&gt; must be in the scope.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;start_url&lt;/code&gt; is relative to the path defined in the &lt;code&gt;scope&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;start_url&lt;/code&gt; starting with &lt;code&gt;/&lt;/code&gt; will always be the root of the origin.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;theme-color&quot;&gt;&lt;code&gt;theme_color&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#theme-color&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;theme_color&lt;/code&gt; sets the color of the tool bar, and may be reflected in
the app&#39;s preview in task switchers. The &lt;code&gt;theme_color&lt;/code&gt; should match the
&lt;code&gt;meta&lt;/code&gt; theme color specified in your document head.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;An example of a PWA window with custom theme_color.&quot; decoding=&quot;async&quot; height=&quot;196&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/8mkBdT3O0FZLo0PUppvv.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    An example of a PWA window with custom theme_color.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As of Chromium 93 and Safari 15, you can adjust this color in a
media query with the &lt;code&gt;media&lt;/code&gt; attribute of the &lt;code&gt;meta&lt;/code&gt; theme color element. The
first one that matches will be picked. For example, you could have one color for
light mode and another one for dark mode. At the time of writing, you can&#39;t
define those in your manifest. See &lt;a href=&quot;https://github.com/w3c/manifest/issues/975&quot; rel=&quot;noopener&quot;&gt;w3c/manifest#975 GitHub
issue&lt;/a&gt;.&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;theme-color&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: light)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;white&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;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;theme-color&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(prefers-color-scheme: dark)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;  &lt;span class=&quot;token attr-name&quot;&gt;content&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;black&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;h4 id=&quot;shortcuts&quot;&gt;&lt;code&gt;shortcuts&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#shortcuts&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;shortcuts&lt;/code&gt; property is an array of &lt;a href=&quot;https://web.dev/app-shortcuts&quot;&gt;app shortcut&lt;/a&gt; objects
whose goal is to provide quick access to key tasks within your app. Each member
is a dictionary that contains at least a &lt;code&gt;name&lt;/code&gt; and a &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;description&quot;&gt;&lt;code&gt;description&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#description&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;description&lt;/code&gt; property describes the purpose of your app.&lt;/p&gt;
&lt;h4 id=&quot;screenshots&quot;&gt;&lt;code&gt;screenshots&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#screenshots&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;screenshots&lt;/code&gt; property is an array of image objects representing your app
in common usage scenarios. Each object must include the &lt;code&gt;src&lt;/code&gt;, a &lt;code&gt;sizes&lt;/code&gt;
property, and the &lt;code&gt;type&lt;/code&gt; of image.  The &lt;code&gt;form_factor&lt;/code&gt; property is optional.
You can set it either to &lt;code&gt;&amp;quot;wide&amp;quot;&lt;/code&gt; for screenshots applicable to wide screens
only or &lt;code&gt;&amp;quot;narrow&amp;quot;&lt;/code&gt; for narrow screenshots. You should only use it when the
layout varies by screen size.&lt;/p&gt;
&lt;p&gt;In Chrome, the image must respond to certain criteria:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Width and height must be at least 320px and at most 3840px.&lt;/li&gt;
&lt;li&gt;The maximum dimension can&#39;t be more than 2.3 times as long as the minimum
dimension.&lt;/li&gt;
&lt;li&gt;All screenshots matching the appropriate form factor must have the same
aspect ratio.&lt;/li&gt;
&lt;/ul&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Screenshots of richer installation UI on desktop and mobile&quot; decoding=&quot;async&quot; height=&quot;386&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/vvhSqZboQoZZN9wBvoXq72wzGAf1/5SlCnibmZHqkXdGVgPZY.jpeg?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Richer installation UI on desktop and mobile.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;link-manifest&quot;&gt;Add the web app manifest to your pages &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#link-manifest&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;After creating the manifest, add a &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag to all the pages of your
Progressive Web App. For example:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;manifest&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/manifest.json&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;aside class=&quot;aside flow bg-tertiary-box-bg color-tertiary-box-text&quot;&gt;&lt;p class=&quot;cluster &quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; role=&quot;img&quot; aria-label=&quot;Lightbulb&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;   &lt;path d=&quot;M9 21c0 .55.45 1 1 1h4c.55 0 1-.45 1-1v-1H9v1zm3-19C8.14 2 5 5.14 5 9c0 2.38 1.19 4.47 3 5.74V17c0 .55.45 1 1 1h6c.55 0 1-.45 1-1v-2.26c1.81-1.27 3-3.36 3-5.74 0-3.86-3.14-7-7-7zm2.85 11.1l-.85.6V16h-4v-2.3l-.85-.6A4.997 4.997 0 017 9c0-2.76 2.24-5 5-5s5 2.24 5 5c0 1.63-.8 3.16-2.15 4.1z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Gotchas&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; The request for the manifest is made &lt;strong&gt;without&lt;/strong&gt; credentials (even if it&#39;s on the same domain), thus if the manifest requires credentials, you must include &lt;code&gt;crossorigin=&amp;quot;use-credentials&amp;quot;&lt;/code&gt; in the manifest tag. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;test-manifest&quot;&gt;Test your manifest &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#test-manifest&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To verify your manifest is setup correctly, use the &lt;strong&gt;Manifest&lt;/strong&gt; pane in the
&lt;strong&gt;Application&lt;/strong&gt; panel of Chrome DevTools.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;The application panel in Chrome Devtools with the manifest tab selected.&quot; decoding=&quot;async&quot; height=&quot;601&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/FpIOY0Ak6FAA5xMuB9IT.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Test your manifest in DevTools.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure&gt;
&lt;/figure&gt;
&lt;p&gt;This pane provides a human-readable version of many of your manifest&#39;s
properties, and makes it easy to verify that all of the images are loading
properly.&lt;/p&gt;
&lt;h2 id=&quot;splash-screen&quot;&gt;Splash screens on mobile &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#splash-screen&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When your app first launches on mobile, it can take a moment for the browser
to spin up, and the initial content to begin rendering. Instead of showing a
white screen that may look to the user like the app is stalled, the browser
will show a splash screen until the first paint.&lt;/p&gt;
&lt;p&gt;Chrome automatically creates the splash screen from the manifest
properties, specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;background_color&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;icons&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;background_color&lt;/code&gt; should be the same color as the load page, to provide
a smooth transition from the splash screen to your app.&lt;/p&gt;
&lt;p&gt;Chrome will choose the icon that closely matches the device resolution for the
device. Providing 192px and 512px icons is sufficient for most cases, but
you can provide additional icons for pixel perfection.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/add-manifest/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are several additional properties that can be added to the web app
manifest. Refer to the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/Manifest&quot; rel=&quot;noopener&quot;&gt;MDN Web App Manifest documentation&lt;/a&gt;
for more information.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author><author>
      <name>François Beaufort</name>
    </author><author>
      <name>Thomas Steiner</name>
    </author>
  </entry>
  
  <entry>
    <title>The Cache API: A quick guide</title>
    <link href="https://web.dev/cache-api-quick-guide/"/>
    <updated>2017-10-03T00:00:00Z</updated>
    <id>https://web.dev/cache-api-quick-guide/</id>
    <content type="html" mode="escaped">&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Cache&quot; rel=&quot;noopener&quot;&gt;Cache API&lt;/a&gt; is a system for storing and retrieving network
requests and their corresponding responses. These might be regular requests
and responses created in the course of running your application, or they could
be created solely for the purpose of storing data for later use.&lt;/p&gt;
&lt;p&gt;The Cache API was created to enable service workers to cache network requests
so that they can provide fast responses, regardless of network speed or
availability. However, the API can also be used as a general storage mechanism.&lt;/p&gt;
&lt;h2 id=&quot;where-is-it-available&quot;&gt;Where is it available? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#where-is-it-available&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Cache API is available in &lt;a href=&quot;https://caniuse.com/#feat=mdn-api_cache&quot; rel=&quot;noopener&quot;&gt;all modern browsers&lt;/a&gt;. It is
exposed via the global &lt;code&gt;caches&lt;/code&gt; property, so you can test for the presence of
the API with a simple feature detection:&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; cacheAvailable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;caches&#39;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; self&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;div class=&quot;wdi-browser-compat&quot;&gt;
  &lt;span class=&quot;wdi-browser-compat__label&quot;&gt;Browser support&lt;/span&gt;
  &lt;ul class=&quot;wdi-browser-compat__items&quot;&gt;
    &lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;chrome&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Chrome 40, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      40
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;firefox&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Firefox 41, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      41
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;edge&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Edge 16, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      16
    &lt;/span&gt;
    &lt;/li&gt;&lt;li class=&quot;wdi-browser-compat__item&quot;&gt;
    &lt;span class=&quot;wdi-browser-compat__icon&quot; data-browser=&quot;safari&quot;&gt;
      &lt;span class=&quot;visually-hidden&quot;&gt;Safari 11.1, Supported&lt;/span&gt;
    &lt;/span&gt;
    &lt;span class=&quot;wdi-browser-compat__version&quot; data-compat=&quot;yes&quot; title=&quot;Supported&quot; aria-label=&quot;Supported&quot;&gt;
      11.1
    &lt;/span&gt;
    &lt;/li&gt;
  &lt;/ul&gt;
  &lt;a class=&quot;wdi-browser-compat__link&quot; href=&quot;https://developer.mozilla.org/docs/Web/API/Cache#browser_compatibility&quot; target=&quot;_blank&quot;&gt;Source&lt;/a&gt;
&lt;/div&gt;
&lt;p&gt;The Cache API can be accessed from a window, iframe, worker, or service worker.&lt;/p&gt;
&lt;h2 id=&quot;what-can-be-stored&quot;&gt;What can be stored &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#what-can-be-stored&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The caches only store pairs of &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Request&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Request&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Response&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;Response&lt;/code&gt;&lt;/a&gt; objects, representing HTTP requests and responses,
respectively. However, the requests and responses can contain any kind of data
that can be transferred over HTTP.&lt;/p&gt;
&lt;h3 id=&quot;how-much-can-be-stored&quot;&gt;How much can be stored? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#how-much-can-be-stored&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In short, &lt;strong&gt;a lot&lt;/strong&gt;, at least a couple of hundred megabytes, and potentially
hundreds of gigabytes or more. Browser implementations vary, but the amount
of storage available is usually based on the amount of storage available on
the device.&lt;/p&gt;
&lt;h2 id=&quot;creating-and-opening-a-cache&quot;&gt;Creating and opening a cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#creating-and-opening-a-cache&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To open a cache, use the &lt;code&gt;caches.open(name)&lt;/code&gt; method, passing the name of the
cache as the single parameter. If the named cache does not exist, it is
created. This method returns a &lt;code&gt;Promise&lt;/code&gt; that resolves with the &lt;code&gt;Cache&lt;/code&gt; object.&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; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;my-cache&#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;// do something with cache...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;adding-to-a-cache&quot;&gt;Adding to a cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#adding-to-a-cache&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are three ways to add an item to a cache - &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;addAll&lt;/code&gt;, and &lt;code&gt;put&lt;/code&gt;.
All three methods return a &lt;code&gt;Promise&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cacheadd&quot;&gt;&lt;code&gt;cache.add&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#cacheadd&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, there is &lt;code&gt;cache.add()&lt;/code&gt;. It takes one parameter, either a &lt;code&gt;Request&lt;/code&gt;
or a URL (&lt;code&gt;string&lt;/code&gt;). It makes a request to the network and stores the response
in the cache. If the
fetch fails, or if the status code of the response is not in the 200 range,
then nothing is stored and the &lt;code&gt;Promise&lt;/code&gt; rejects. Note that cross-origin
requests not in CORS mode cannot be stored because they return a &lt;code&gt;status&lt;/code&gt; of
&lt;code&gt;0&lt;/code&gt;. Such requests can only be stored with &lt;code&gt;put&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 comment&quot;&gt;// Retreive data.json from the server and store the response.&lt;/span&gt;&lt;br /&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/data.json&#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;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Retreive data.json from the server and store the response.&lt;/span&gt;&lt;br /&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/data.json&#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;h3 id=&quot;cacheaddall&quot;&gt;&lt;code&gt;cache.addAll&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#cacheaddall&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Next, there is &lt;code&gt;cache.addAll()&lt;/code&gt;. It works similarly to &lt;code&gt;add()&lt;/code&gt;, but takes an
array of &lt;code&gt;Request&lt;/code&gt; objects or URLs (&lt;code&gt;string&lt;/code&gt;s). This works similarly to
calling &lt;code&gt;cache.add&lt;/code&gt; for each individual request, except that the &lt;code&gt;Promise&lt;/code&gt;
rejects if any single request is not cached.&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; urls &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/weather/today.json&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/weather/tomorrow.json&#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;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;urls&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;In each of these cases, a new entry overwrites any matching existing entry.
This uses the same matching rules described in the section on
&lt;a href=&quot;https://web.dev/cache-api-quick-guide/#retrieving&quot;&gt;retrieving&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;cacheput&quot;&gt;&lt;code&gt;cache.put&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#cacheput&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Finally, there is &lt;code&gt;cache.put()&lt;/code&gt;, which allows you to store either a response
from the network, or create and store your own &lt;code&gt;Response&lt;/code&gt;. It takes two
parameters. The first can either be a &lt;code&gt;Request&lt;/code&gt; object or a URL (&lt;code&gt;string&lt;/code&gt;).
The second must be a &lt;code&gt;Response&lt;/code&gt;, either from the network, or generated by your
code.&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 comment&quot;&gt;// Retrieve data.json from the server and store the response.&lt;/span&gt;&lt;br /&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/data.json&#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 comment&quot;&gt;// Create a new entry for test.json and store the newly created response.&lt;/span&gt;&lt;br /&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/test.json&#39;&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;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{&quot;foo&quot;: &quot;bar&quot;}&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class=&quot;token comment&quot;&gt;// Retrieve data.json from the 3rd party site and store the response.&lt;/span&gt;&lt;br /&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;https://example.com/data.json&#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;The &lt;code&gt;put()&lt;/code&gt; method is more permissive than either &lt;code&gt;add()&lt;/code&gt; or &lt;code&gt;addAll()&lt;/code&gt;, and
will allow you to store non-CORS responses, or other responses where the status
code of the response is not in the 200 range. It will overwrite any previous
responses for the same request.&lt;/p&gt;
&lt;h4 id=&quot;creating-request-objects&quot;&gt;Creating Request objects &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#creating-request-objects&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Create the &lt;code&gt;Request&lt;/code&gt; object using a URL for the thing being stored:&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; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/my-data-store/item-id&#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;h4 id=&quot;working-with-response-objects&quot;&gt;Working with Response objects &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#working-with-response-objects&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;Response&lt;/code&gt; object constructor accepts many types of data, including
&lt;code&gt;Blob&lt;/code&gt;s, &lt;code&gt;ArrayBuffer&lt;/code&gt;s, &lt;code&gt;FormData&lt;/code&gt; objects, and strings.&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; imageBlob &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Blob&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;image/jpeg&#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;const&lt;/span&gt; imageResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imageBlob&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; stringResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Hello world&#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;You can set the MIME type of a &lt;code&gt;Response&lt;/code&gt; by setting the appropriate header.&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; options &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;headers&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 string-property property&quot;&gt;&#39;Content-Type&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;application/json&#39;&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;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jsonResponse &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&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; options&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 you have retrieved a &lt;code&gt;Response&lt;/code&gt; and wish to access its body, there are
several helper methods you can use. Each returns a &lt;code&gt;Promise&lt;/code&gt; that resolves
with a value of a different type.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;th&gt;Method&lt;/th&gt;
    &lt;th&gt;Description&lt;/th&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;arrayBuffer&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Returns an &lt;code&gt;ArrayBuffer&lt;/code&gt; containing the body, serialized to
        bytes.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;blob&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Returns a &lt;code&gt;Blob&lt;/code&gt;. If the &lt;code&gt;Response&lt;/code&gt; was created
        with a &lt;code&gt;Blob&lt;/code&gt; then this new &lt;code&gt;Blob&lt;/code&gt; has the same
        type. Otherwise, the &lt;code&gt;Content-Type&lt;/code&gt; of the
        &lt;code&gt;Response&lt;/code&gt; is used.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Interprets the bytes of the body as a UTF-8 encoded string.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;json&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Interprets the bytes of the body as a UTF-8 encoded string, then tries
        to parse it as JSON. Returns the resulting object, or throws a
        &lt;code&gt;TypeError&lt;/code&gt; if the string cannot be parsed as JSON.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;formData&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Interprets the bytes of the body as an HTML form, encoded either as
        &lt;code&gt;multipart/form-data&lt;/code&gt; or
        &lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;. Returns a
        &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FormData&quot;&gt;FormData&lt;/a&gt;
        object, or throws a &lt;code&gt;TypeError&lt;/code&gt; if the data cannot be parsed.
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;&lt;code&gt;body&lt;/code&gt;&lt;/td&gt;
      &lt;td&gt;
        Returns a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/ReadableStream&quot;&gt;ReadableStream&lt;/a&gt;
        for the body data.
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;For example&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; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Hello world&#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;const&lt;/span&gt; buffer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;arrayBuffer&lt;/span&gt;&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;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Uint8Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buffer&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 comment&quot;&gt;// Uint8Array(11) [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;retrieving&quot;&gt;Retrieving from a cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#retrieving&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To find an item in a cache, you can use the &lt;code&gt;match&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;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; response&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 &lt;code&gt;request&lt;/code&gt; is a string the browser converts it to a &lt;code&gt;Request&lt;/code&gt; by calling
&lt;code&gt;new Request(request)&lt;/code&gt;. The function returns a &lt;code&gt;Promise&lt;/code&gt; that resolves to
a &lt;code&gt;Response&lt;/code&gt; if a matching entry is found, or &lt;code&gt;undefined&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;p&gt;To determine if two &lt;code&gt;Requests&lt;/code&gt; match, the browser uses more than just the URL. Two
requests are considered different if they have different query strings,
&lt;code&gt;Vary&lt;/code&gt; headers, or HTTP methods (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, etc.).&lt;/p&gt;
&lt;p&gt;You can ignore some or all of these things by passing an options object as a
second parameter.&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; options &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;ignoreSearch&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 literal-property property&quot;&gt;ignoreMethod&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 literal-property property&quot;&gt;ignoreVary&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;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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&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;// do something with the response&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;If more than one cached request matches then the one that was created first is
returned. If you want to retrieve &lt;em&gt;all&lt;/em&gt; matching responses, you can use
&lt;code&gt;cache.matchAll()&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;const&lt;/span&gt; options &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;ignoreSearch&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 literal-property property&quot;&gt;ignoreMethod&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 literal-property property&quot;&gt;ignoreVary&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;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;br /&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; responses &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;matchAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;There are &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;responses&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; matching responses.&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As a shortcut you can search over all caches at once by using &lt;code&gt;caches.match()&lt;/code&gt;
instead of calling &lt;code&gt;cache.match()&lt;/code&gt; for each cache.&lt;/p&gt;
&lt;h2 id=&quot;searching&quot;&gt;Searching &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#searching&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Cache API does not provide a way to search for requests or responses
except for matching entries against a &lt;code&gt;Response&lt;/code&gt; object. However, you can
implement your own search using filtering or by creating an index.&lt;/p&gt;
&lt;h3 id=&quot;filtering&quot;&gt;Filtering &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#filtering&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One way to implement your own search is to iterate over all entries and
filter down to the ones that you want. Let&#39;s say that you want to find all
items that have URLs ending with &lt;code&gt;.png&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;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;findImages&lt;/span&gt;&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 comment&quot;&gt;// Get a list of all of the caches for this origin&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cacheNames &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&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;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; cacheNames&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;// Open the cache&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; cache &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; caches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;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;br /&gt;    &lt;span class=&quot;token comment&quot;&gt;// Get a list of entries. Each item is a Request object&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; request &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;      &lt;span class=&quot;token comment&quot;&gt;// If the request URL matches, add the response to the result&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;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;.png&#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;        result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;match&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&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;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; result&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;This way you can use any property of the &lt;code&gt;Request&lt;/code&gt; and &lt;code&gt;Response&lt;/code&gt; objects to
filter the entries. Note that this is slow if you search over large sets of
data.&lt;/p&gt;
&lt;h3 id=&quot;creating-an-index&quot;&gt;Creating an index &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#creating-an-index&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The other way to implement your own search is to maintain a separate index of
entries that can be searched and store the index in IndexedDB. Since this is the kind of
operation that IndexedDB was designed for it has much better performance with
large numbers of entries.&lt;/p&gt;
&lt;p&gt;If you store the URL of the &lt;code&gt;Request&lt;/code&gt; alongside the searchable properties
then you can easily retrieve the correct cache entry after doing the search.&lt;/p&gt;
&lt;h2 id=&quot;deleting-an-item&quot;&gt;Deleting an item &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#deleting-an-item&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To delete an item from a cache:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&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;Where request can be a &lt;code&gt;Request&lt;/code&gt; or a URL string. This method also takes the
same options object as &lt;code&gt;cache.match&lt;/code&gt;, which allows you to delete multiple
&lt;code&gt;Request&lt;/code&gt;/&lt;code&gt;Response&lt;/code&gt; pairs for the same URL.&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;cache&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;delete&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/example/file.txt&#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 literal-property property&quot;&gt;ignoreVary&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;span class=&quot;token literal-property property&quot;&gt;ignoreSearch&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;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;h2 id=&quot;deleting-a-cache&quot;&gt;Deleting a cache &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#deleting-a-cache&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To delete a cache, call &lt;code&gt;caches.delete(name)&lt;/code&gt;. This function returns a
&lt;code&gt;Promise&lt;/code&gt; that resolves to &lt;code&gt;true&lt;/code&gt; if the cache existed and was deleted, or
&lt;code&gt;false&lt;/code&gt; otherwise.&lt;/p&gt;
&lt;h2 id=&quot;thanks&quot;&gt;Thanks &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/cache-api-quick-guide/#thanks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Thanks to Mat Scales who wrote the original version of this article, which
first appeared on WebFundamentals.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>WebAPKs on Android</title>
    <link href="https://web.dev/webapks/"/>
    <updated>2017-05-21T00:00:00Z</updated>
    <id>https://web.dev/webapks/</id>
    <content type="html" mode="escaped">&lt;p&gt;&lt;a href=&quot;https://web.dev/progressive-web-apps/&quot;&gt;Installing a PWA&lt;/a&gt; on Android does
more than just add the Progressive Web App to the user&#39;s Home Screen. Chrome
automatically generates and installs a special APK of your app. We sometimes
refer to this as a &lt;strong&gt;WebAPK&lt;/strong&gt;. Being installed via an APK makes it possible
for your app to show up in the app launcher, in Android&#39;s app settings and
to register a set of intent filters.&lt;/p&gt;
&lt;p&gt;To
&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/chrome/android/webapk/README.md&quot; rel=&quot;noopener&quot;&gt;generate the WebAPK&lt;/a&gt;,
Chrome looks at the &lt;a href=&quot;https://web.dev/add-manifest/&quot;&gt;web app manifest&lt;/a&gt; and
other metadata. &lt;a href=&quot;https://web.dev/webapks/#update-webapk&quot;&gt;When an update to the manifest is detected&lt;/a&gt;,
Chrome will need to generate a new APK.&lt;/p&gt;
&lt;p&gt;Note: Since the WebAPK is regenerated each time an updated manifest is detected,
we recommend changing it only when necessary. Don&#39;t use the manifest to store
user specific identifiers, or other other data that might be customized.
Frequently changing the manifest will increase the overall install time.&lt;/p&gt;
&lt;h2 id=&quot;android-intent-filters&quot;&gt;Android intent filters &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#android-intent-filters&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When a Progressive Web App is installed on Android, it will register a set of
&lt;a href=&quot;https://developer.android.com/guide/components/intents-filters&quot; rel=&quot;noopener&quot;&gt;intent filters&lt;/a&gt;
for all URLs within the scope of the app. When a user clicks on a link that
is within the scope of the app, the app will be opened, rather than opening
within a browser tab.&lt;/p&gt;
&lt;p&gt;Consider the following partial &lt;code&gt;manifest.json&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;start_url&quot;: &quot;/&quot;,&lt;br /&gt;&quot;display&quot;: &quot;standalone&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When a web app using it is launched from the app launcher, it would open
&lt;code&gt;https://example.com/&lt;/code&gt; as a standalone app, without any browser chrome.&lt;/p&gt;
&lt;p&gt;The WebAPK would include the following intent filters:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;intent-filter&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;action&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.action.VIEW&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;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.category.DEFAULT&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;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.category.BROWSABLE&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;data&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;scheme&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;https&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;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;host&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;example.com&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;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;pathPrefix&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;/&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;intent-filter&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;If the user clicks on a link within an installed app to
&lt;code&gt;https://example.com/read&lt;/code&gt;, it would be caught by the intent and opened
in the Progressive Web App.&lt;/p&gt;
&lt;p&gt;Note: Navigating directly to &lt;code&gt;https://example.com/app/&lt;/code&gt; from the address bar in
Chrome will work exactly the same as it does for native apps that have an
intent filter. Chrome assumes the user &lt;strong&gt;intended&lt;/strong&gt; to visit the site and
will open this site.&lt;/p&gt;
&lt;h3 id=&quot;using-scope-to-restrict-intent-filters&quot;&gt;Using &lt;code&gt;scope&lt;/code&gt; to restrict intent filters &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#using-scope-to-restrict-intent-filters&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you don&#39;t want your Progressive Web App to handle all URLs within your site,
you can add the &lt;a href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/a&gt; property to
your web app manifest. The &lt;code&gt;scope&lt;/code&gt; property tells Android to only open your web
app if the URL matches the &lt;code&gt;origin&lt;/code&gt; + &lt;code&gt;scope&lt;/code&gt;. It gives you control over which
URLs will be handled by your app, and which should be opened in the browser.
This is helpful when you have your app and other non-app content on the same
domain.&lt;/p&gt;
&lt;p&gt;Consider the following partial &lt;code&gt;manifest.json&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&quot;scope&quot;: &quot;/app/&quot;,&lt;br /&gt;&quot;start_url&quot;: &quot;/app/&quot;,&lt;br /&gt;&quot;display&quot;: &quot;standalone&quot;,&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;When launched from the app launcher, it would open &lt;code&gt;https://example.com/app/&lt;/code&gt;
as a standalone app, without any browser chrome.&lt;/p&gt;
&lt;p&gt;Like before, the generated WebAPK would include an intent filter, but with a
different &lt;code&gt;android:pathPrefix&lt;/code&gt; attribute in the APK&#39;s &lt;code&gt;AndroidManifest.xml&lt;/code&gt;:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&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;intent-filter&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;action&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.action.VIEW&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;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.category.DEFAULT&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;category&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;name&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;android.intent.category.BROWSABLE&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;data&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;scheme&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;https&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;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;host&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;example.com&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;&lt;span class=&quot;token namespace&quot;&gt;android:&lt;/span&gt;pathPrefix&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;/app/&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;intent-filter&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;Let&#39;s take a look at a few examples:&lt;br /&gt;&lt;/p&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://example.com/app/&lt;/code&gt; - within &lt;code&gt;/app/&lt;/code&gt;&lt;/p&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;better&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Do&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://example.com/app/read/book&lt;/code&gt; - within &lt;code&gt;/app/&lt;/code&gt;&lt;/p&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://example.com/help/&lt;/code&gt; - not in &lt;code&gt;/app/&lt;/code&gt;&lt;/p&gt;
&lt;/figure&gt;
&lt;figure class=&quot;compare flow&quot; data-type=&quot;worse&quot; data-size=&quot;full&quot;&gt;&lt;p class=&quot;compare__label&quot;&gt;Don&#39;t&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://example.com/about/&lt;/code&gt; - not in &lt;code&gt;/app/&lt;/code&gt;&lt;/p&gt;
&lt;/figure&gt;
&lt;p&gt;See &lt;a href=&quot;https://web.dev/add-manifest/#scope&quot;&gt;&lt;code&gt;scope&lt;/code&gt;&lt;/a&gt; for more information about
&lt;code&gt;scope&lt;/code&gt;, what happens when you don&#39;t set it, and how you can use it to define
the scope of your app.&lt;/p&gt;
&lt;h2 id=&quot;managing-permissions&quot;&gt;Managing permissions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#managing-permissions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Permissions work in the same way as other web apps and cannot be requested at
install time. Instead, they must be requested at run time, ideally only when
you really need them. For example, don&#39;t ask for camera permission on first
load, but instead wait until the user attempts to take a picture.&lt;/p&gt;
&lt;p&gt;Note: Android normally grants immediate permission to show notifications for
installed apps, but apps installed via WebAPKs are not granted this at install
time; you must request it at runtime within your app.&lt;/p&gt;
&lt;h2 id=&quot;managing-storage-and-app-state&quot;&gt;Managing storage and app state &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#managing-storage-and-app-state&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even though the progressive web app is installed via an APK, Chrome uses the
current profile to store any data, and it will not be segregated away. This
allows a shared experience between the browser and the installed app. Cookies
are shared and active, any client side storage is accessible and the service
worker is installed and ready to go.&lt;/p&gt;
&lt;p&gt;Note: Keep in mind that if the user clears their Chrome profile, or chooses
to delete site data, that will apply to the WebAPK as well.&lt;/p&gt;
&lt;h2 id=&quot;updating-the-webapk&quot;&gt;Updating the WebAPK &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#updating-the-webapk&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Information on how a WebAPK is updated has moved to
&lt;a href=&quot;https://web.dev/manifest-updates/&quot;&gt;How Chrome handles updates to the web app manifest&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/webapks/#frequently-asked-questions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;dl&gt;
&lt;dt&gt;What icons are used to generate the splash screen? :&lt;/dt&gt;
&lt;dd&gt;We recommend you provide at least two icons:
&lt;a href=&quot;https://web.dev/add-manifest/#splash-screen&quot;&gt;192px and 512px&lt;/a&gt; for the splash screen.
We heard from you that icons
on the splash screen were too small. WebAPKs generated in Chrome 71 or later
will show a larger icon on the splash screen. No action is required, as
long as the recommended icons are provided.&lt;/dd&gt;
&lt;dt&gt;What happens if the user has already installed the native app for the site?&lt;/dt&gt;
&lt;dd&gt;Like add to home screen today, users will be able to add a site independent
of any native apps. If you expect users to potentially install both, we
recommend differentiating the icon or name of your site from your native
app.&lt;/dd&gt;
&lt;dt&gt;Will my installed site&#39;s storage be cleared if the user clears Chrome&#39;s cache?&lt;/dt&gt;
&lt;dd&gt;Yes.&lt;/dd&gt;
&lt;dt&gt;Will my app be re-installed when I get a new device?&lt;/dt&gt;
&lt;dd&gt;Not at this time, but we think it is an important area and we are
investigating ways to make it work.&lt;/dd&gt;
&lt;dt&gt;How are permissions handled? Will I see the Chrome prompt or Android&#39;s?&lt;/dt&gt;
&lt;dd&gt;Permissions will still be managed through Chrome. Users will see the Chrome
prompts to grant permissions and will be able to edit them in Chrome
settings.&lt;/dd&gt;
&lt;dt&gt;What versions of Android will this work on?&lt;/dt&gt;
&lt;dd&gt;Progressive web apps can be installed on all versions of Android that
run Chrome for Android, specifically Jelly Bean and above.&lt;/dd&gt;
&lt;dt&gt;Does this use the WebView?&lt;/dt&gt;
&lt;dd&gt;No, the site opens in the version of Chrome the user added the site from.&lt;/dd&gt;
&lt;dt&gt;Can we upload the APKs that are created to the Play Store?&lt;/dt&gt;
&lt;dd&gt;No. If you want to upload your own APK, check out&lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;Trusted Web Activities&lt;/a&gt;.&lt;/dd&gt;
&lt;dt&gt;Are these listed in the Play Store?&lt;/dt&gt;
&lt;dd&gt;No. If you want to upload your own APK for listing in the Play Store, check
out &lt;a href=&quot;https://developer.chrome.com/docs/android/trusted-web-activity/&quot; rel=&quot;noopener&quot;&gt;Trusted Web Activities&lt;/a&gt;.&lt;/dd&gt;
&lt;dt&gt;I am a developer of another browser on Android, can I have this seamless install process? :&lt;/dt&gt;
&lt;dd&gt;We are working on it. We are committed to making this available to all
browsers on Android and we will have more details soon.&lt;/dd&gt;
&lt;/dl&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Basics of UX</title>
    <link href="https://web.dev/push-notifications-video/"/>
    <updated>2016-06-30T00:00:00Z</updated>
    <id>https://web.dev/push-notifications-video/</id>
    <content type="html" mode="escaped">&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;_dXBibRO0SM&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Push notifications are an incredibly effective way to build deeper user
engagement with your application, and are now available on the web. In this
video, we&#39;ll take a look at how they work and deep-dive into how to
implement push notifications in web applications, from beginning to end.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Click to Call</title>
    <link href="https://web.dev/click-to-call/"/>
    <updated>2014-06-17T00:00:00Z</updated>
    <id>https://web.dev/click-to-call/</id>
    <content type="html" mode="escaped">&lt;p&gt;On devices with phone capabilities, make it easy for users to directly connect
with you by simply tapping a phone number, more commonly known as click to call.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Wrap all phone numbers in hyperlinks with the &lt;code&gt;tel:&lt;/code&gt; schema.&lt;/li&gt;
&lt;li&gt;Always use the international dialing format.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;link-telephone-numbers-for-click-to-call&quot;&gt;Link telephone numbers for click to call &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#link-telephone-numbers-for-click-to-call&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;While many modern mobile browsers automatically detect phone numbers
and convert them to links, it’s a good idea to do this directly in your code.
By manually tagging each phone number, you can ensure that phone numbers are always
enabled for click to call and that they will be styled to match your site.&lt;/p&gt;
&lt;p&gt;To mark a phone number as a link, use the &lt;code&gt;tel:&lt;/code&gt; scheme. The syntax is
simple:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;NIST Telephone Time-of-Day Service&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;a&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;tel:+1-303-499-7111&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;+1 (303) 499-7111&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Your browser displays this syntax as follows:&lt;/p&gt;
&lt;p&gt;NIST Telephone Time-of-Day Service &lt;a href=&quot;tel:+1-303-499-7111&quot;&gt;+1 (303) 499-7111&lt;/a&gt;&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Click to call example&quot; decoding=&quot;async&quot; height=&quot;600&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/SzyqOLvdN3jeZkRHJjai.jpeg?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
  &lt;figcaption&gt;Click to call example&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;On most devices with telephone capabilities, the user receives a
confirmation before the number is dialed, to ensure that the user isn&#39;t being
tricked into calling an expensive long distance or premium phone number.
When the device doesn’t support phone calls, users may be presented with a
menu allowing them to choose how the browser should handle the number.&lt;/p&gt;
&lt;p&gt;Desktop browsers that don’t support voice calls open the default
telephony app on the computer; for example Google Voice or Microsoft
Communicator.&lt;/p&gt;
&lt;h2 id=&quot;use-the-international-dialing-format&quot;&gt;Use the international dialing format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#use-the-international-dialing-format&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Always supply the phone number using the international dialing format:
the plus sign (&lt;code&gt;+&lt;/code&gt;), country code, area code, and number. While not absolutely
necessary, it’s a good idea to separate each segment of the number with a
hyphen (&lt;code&gt;-&lt;/code&gt;) for easier reading and better auto-detection.&lt;/p&gt;
&lt;p&gt;Using a hyphenated international dialing format ensures that no matter where
the user is calling from, whether a few hundred meters away or thousands
of kilometers, their call will be connected.&lt;/p&gt;
&lt;h2 id=&quot;disable-auto-detection-when-necessary&quot;&gt;Disable auto-detection when necessary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#disable-auto-detection-when-necessary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Modern mobile browsers automatically detect phone numbers and enable
click to call. Mobile Safari automatically converts phone numbers to links
with the associated hyperlink styles. Chrome for Android automatically
detects phone numbers and allows users to click to call, but does not wrap
the phone numbers in hyperlinks or apply any special styles.&lt;/p&gt;
&lt;p&gt;To prevent Mobile Safari from automatically detecting phone numbers, add the
following meta tag to your page:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&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;format-detection&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&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;telephone=no&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;h2 id=&quot;other-click-to-call-features&quot;&gt;Other click to call features &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#other-click-to-call-features&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In addition to the &lt;code&gt;tel:&lt;/code&gt; schema, some modern browsers also support the &lt;code&gt;sms:&lt;/code&gt;
and &lt;code&gt;mms:&lt;/code&gt; schemas, though support is not as consistent, and some
features like setting the message body don&#39;t always work.&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/click-to-call/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Device Orientation &amp; Motion</title>
    <link href="https://web.dev/device-orientation/"/>
    <updated>2014-06-17T00:00:00Z</updated>
    <id>https://web.dev/device-orientation/</id>
    <content type="html" mode="escaped">&lt;p&gt;Device motion and orientation events provide access to the built-in
accelerometer, gyroscope, and compass in mobile devices.&lt;/p&gt;
&lt;p&gt;These events can be used for many purposes; in gaming, for example, to control
the direction or action of a character. When used with geolocation, they can
help create more accurate turn-by-turn navigation or provide information about
a specific location.&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Not all browsers use the same coordinate system, and they may report different values under identical situations. This has improved over time, but be sure to test your situation. &lt;/div&gt;&lt;/aside&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#summary&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Detect which side of the device is up and how the device is rotating.&lt;/li&gt;
&lt;li&gt;Learn when and how to respond to motion and orientation events.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;which-end-is-up&quot;&gt;Which end is up? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#which-end-is-up&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To use the data that the device orientation and motion events return,
it is important to understand the values provided.&lt;/p&gt;
&lt;h3 id=&quot;earth-coordinate-frame&quot;&gt;Earth coordinate frame &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#earth-coordinate-frame&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Earth coordinate frame, described by the values &lt;code&gt;X&lt;/code&gt;, &lt;code&gt;Y&lt;/code&gt;, and &lt;code&gt;Z&lt;/code&gt;, is aligned
based on gravity and standard magnetic orientation.&lt;/p&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Coordinate system&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;code&gt;X&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;Represents the east-west direction (where east is positive).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;code&gt;Y&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;Represents the north-south direction (where north is positive).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;&lt;code&gt;Z&lt;/code&gt;&lt;/td&gt;
  &lt;td&gt;Represents the up-down direction, perpendicular to the ground
      (where up is positive).
  &lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;h3 id=&quot;device-coordinate-frame&quot;&gt;Device coordinate frame &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#device-coordinate-frame&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
    &lt;img alt=&quot;Illustration of device coordinate frame&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/yyBk2geW7lIKqULH5ZIT.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
    &lt;figcaption&gt;
        Illustration of device coordinate frame
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;!-- Special thanks to Sheppy (https://developer.mozilla.org/en-US/profiles/Sheppy)
  for his images which are in the public domain. --&gt;
&lt;p&gt;The device coordinate frame, described by the values &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;y&lt;/code&gt;, and &lt;code&gt;z&lt;/code&gt;, is aligned
based on the center of the device.&lt;/p&gt;
&lt;table&gt;
    &lt;tr&gt;&lt;th colspan=&quot;2&quot;&gt;Coordinate system&lt;/th&gt;&lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;X&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;In the plane of the screen, positive to the right.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;Y&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;In the plane of the screen, positive towards the top.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
    &lt;td&gt;&lt;code&gt;Z&lt;/code&gt;&lt;/td&gt;
    &lt;td&gt;Perpendicular to the screen or keyboard, positive extending
        away.
    &lt;/td&gt;
    &lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;On a phone or tablet, the orientation of the device is based on the typical
orientation of the screen. For phones and tablets, it is based on the device
being in portrait mode. For desktop or laptop computers, the orientation is
considered in relation to the keyboard.&lt;/p&gt;
&lt;h3 id=&quot;rotation-data&quot;&gt;Rotation data &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#rotation-data&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rotation data is returned as a &lt;a href=&quot;https://en.wikipedia.org/wiki/Euler_angles&quot; rel=&quot;noopener&quot;&gt;Euler angle&lt;/a&gt;,
representing the number of degrees of difference between the device coordinate
frame and the Earth coordinate frame.&lt;/p&gt;
&lt;h4 id=&quot;alpha&quot;&gt;Alpha &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#alpha&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;Illustration of alpha in the device coordinate frame&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/PoeibnLAXFFzDOoPNLzJ.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
    &lt;figcaption&gt;
      Illustration of alpha in the device coordinate frame
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;The rotation around the z axis. The &lt;code&gt;alpha&lt;/code&gt; value is 0° when the top of
the device is pointed directly north. As the device is rotated counter-clockwise,
the &lt;code&gt;alpha&lt;/code&gt; value increases.&lt;/p&gt;
&lt;h4 id=&quot;beta&quot;&gt;Beta &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#beta&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;figure&gt;
    &lt;img alt=&quot;illustration of device coordinate frame&quot; decoding=&quot;async&quot; height=&quot;250&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 400px) 400px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/1h9NxeN7yae1Yp4h3xj1.png?auto=format&amp;w=800 800w&quot; width=&quot;400&quot; /&gt;
    &lt;figcaption&gt;
        Illustration of beta in the device coordinate frame
    &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The rotation around the x axis. The &lt;code&gt;beta&lt;/code&gt; value is 0° when the top and
bottom of the device are equidistant from the surface of the earth. The value
increases as the top of the device is tipped toward the surface of the earth.&lt;/p&gt;
&lt;h4 id=&quot;gamma&quot;&gt;Gamma &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#gamma&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
  &lt;figure&gt;
    &lt;img alt=&quot;illustration of device coordinate frame&quot; decoding=&quot;async&quot; height=&quot;388&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 415px) 415px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/C47gYyWYVMMhDmtYSLOWazuyePF2/kqDO9oumteoknSlF6lGq.png?auto=format&amp;w=830 830w&quot; width=&quot;415&quot; /&gt;
    &lt;figcaption&gt;
      Illustration of gamma in the device coordinate frame
    &lt;/figcaption&gt;
  &lt;/figure&gt;
&lt;p&gt;The rotation around the y axis. The &lt;code&gt;gamma&lt;/code&gt; value is 0° when the left and
right edges of the device are equidistant from the surface of the earth. The value
increases as the right side is tipped towards the surface of the earth.&lt;/p&gt;
&lt;div style=&quot;clear:both;&quot;&gt;&lt;/div&gt;
&lt;h2 id=&quot;device-orientation&quot;&gt;Device orientation &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#device-orientation&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The device orientation event returns rotation data, which includes how much
the device is leaning front-to-back, side-to-side, and, if the phone or laptop
has a compass, the direction the device is facing.&lt;/p&gt;
&lt;p&gt;Use sparingly.
Test for support.
Don&#39;t update the UI on every orientation event; instead, sync to &lt;code&gt;requestAnimationFrame&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use-device-orientation-events&quot;&gt;When to use device orientation events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#when-to-use-device-orientation-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are several uses for device orientation events. Examples include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update a map as the user moves.&lt;/li&gt;
&lt;li&gt;Subtle UI tweaks, for example, adding parallax effects.&lt;/li&gt;
&lt;li&gt;Combined with geolocation, can be used for turn-by-turn navigation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;check-for-support-and-listen-for-events&quot;&gt;Check for support and listen for events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#check-for-support-and-listen-for-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To listen for &lt;code&gt;DeviceOrientationEvent&lt;/code&gt;, first check to see if the browser supports the events. Then, attach an event listener to the &lt;code&gt;window&lt;/code&gt; object listening for &lt;code&gt;deviceorientation&lt;/code&gt; 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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DeviceOrientationEvent&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;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;deviceorientation&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deviceOrientationHandler&lt;span class=&quot;token punctuation&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;  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;doeSupported&#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;innerText &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Supported!&#39;&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;h3 id=&quot;handle-the-device-orientation-events&quot;&gt;Handle the device orientation events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#handle-the-device-orientation-events&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The device orientation event fires when the device moves or changes
orientation. It returns data about the difference between the device in
its current position in relation to the
&lt;a href=&quot;https://web.dev/device-orientation/#earth-coordinate-frame&quot;&gt;Earth coordinate frame&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The event typically returns three properties: &lt;a href=&quot;https://web.dev/device-orientation/#alpha&quot;&gt;&lt;code&gt;alpha&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/device-orientation/#beta&quot;&gt;&lt;code&gt;beta&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://web.dev/device-orientation/#gamma&quot;&gt;&lt;code&gt;gamma&lt;/code&gt;&lt;/a&gt;. On Mobile Safari, an additional parameter
&lt;a href=&quot;https://developer.apple.com/library/ios/documentation/SafariDOMAdditions/Reference/DeviceOrientationEventClassRef/&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;webkitCompassHeading&lt;/code&gt;&lt;/a&gt;
is returned with the compass heading.&lt;/p&gt;
&lt;h4 id=&quot;device-motion&quot;&gt;Device motion &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#device-motion&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The device orientation event returns rotation data, which includes how much
the device is leaning front-to-back, side-to-side, and, if the phone or laptop
has a compass, the direction the device is facing.&lt;/p&gt;
&lt;p&gt;Use device motion for when the current motion of the device is needed.
&lt;code&gt;rotationRate&lt;/code&gt; is provided in °/sec.
&lt;code&gt;acceleration&lt;/code&gt; and &lt;code&gt;accelerationWithGravity&lt;/code&gt; are provided in m/sec&lt;sup&gt;2&lt;/sup&gt;.
Be aware of differences between browser implementations.&lt;/p&gt;
&lt;h4 id=&quot;when-to-use-device-motion-events&quot;&gt;When to use device motion events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#when-to-use-device-motion-events&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are several uses for device motion events. Examples include the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Shake gesture to refresh data.&lt;/li&gt;
&lt;li&gt;In games, to cause characters to jump or move.&lt;/li&gt;
&lt;li&gt;For health and fitness apps.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;check-for-support-and-listen-for-events-2&quot;&gt;Check for support and listen for events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#check-for-support-and-listen-for-events-2&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To listen for &lt;code&gt;DeviceMotionEvent&lt;/code&gt;, first check to see if the events are
supported in the browser. Then attach an event listener to the &lt;code&gt;window&lt;/code&gt;
object listening for &lt;code&gt;devicemotion&lt;/code&gt; 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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DeviceMotionEvent&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;  window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addEventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;devicemotion&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; deviceMotionHandler&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 function&quot;&gt;setTimeout&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stopJump&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token 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;h4 id=&quot;handle-the-device-motion-events&quot;&gt;Handle the device motion events &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#handle-the-device-motion-events&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The device motion event fires on a regular interval and returns data about the
rotation (in °/second) and acceleration (in m/second&lt;sup&gt;2&lt;/sup&gt;)
of the device, at that moment in time. Some devices do not have the hardware
to exclude the effect of gravity.&lt;/p&gt;
&lt;p&gt;The event returns four properties,
&lt;a href=&quot;https://web.dev/device-orientation/#device-coordinate-frame&quot;&gt;&lt;code&gt;accelerationIncludingGravity&lt;/code&gt;&lt;/a&gt;,
&lt;a href=&quot;https://web.dev/device-orientation/#device-coordinate-frame&quot;&gt;&lt;code&gt;acceleration&lt;/code&gt;&lt;/a&gt;, which excludes the effects of
gravity, &lt;a href=&quot;https://web.dev/device-orientation/#rotation-data&quot;&gt;&lt;code&gt;rotationRate&lt;/code&gt;&lt;/a&gt;, and &lt;code&gt;interval&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, let&#39;s take a look at a phone, lying on a flat table, with its
screen facing up.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th data-th=&quot;State&quot;&gt;State&lt;/th&gt;
      &lt;th data-th=&quot;Rotation&quot;&gt;Rotation&lt;/th&gt;
      &lt;th data-th=&quot;Acceleration (m/s&lt;sup&gt;2&lt;/sup&gt;)&quot;&gt;Acceleration (m/s&lt;sup&gt;2&lt;/sup&gt;)&lt;/th&gt;
      &lt;th data-th=&quot;Acceleration with gravity (m/s&lt;sup&gt;2&lt;/sup&gt;)&quot;&gt;Acceleration with gravity (m/s&lt;sup&gt;2&lt;/sup&gt;)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Not moving&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[0, 0, 9.8]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving up towards the sky&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[0, 0, 5]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[0, 0, 14.81]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving only to the right&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[3, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[3, 0, 9.81]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving up and to the right&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[5, 0, 5]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[5, 0, 14.81]&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Conversely, if the phone were held so the screen was perpendicular to the
ground, and was directly visible to the viewer:&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th data-th=&quot;State&quot;&gt;State&lt;/th&gt;
      &lt;th data-th=&quot;Rotation&quot;&gt;Rotation&lt;/th&gt;
      &lt;th data-th=&quot;Acceleration (m/s&lt;sup&gt;2&lt;/sup&gt;)&quot;&gt;Acceleration (m/s&lt;sup&gt;2&lt;/sup&gt;)&lt;/th&gt;
      &lt;th data-th=&quot;Acceleration with gravity (m/s&lt;sup&gt;2&lt;/sup&gt;)&quot;&gt;Acceleration with gravity (m/s&lt;sup&gt;2&lt;/sup&gt;)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Not moving&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[0, 9.81, 0]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving up towards the sky&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[0, 5, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[0, 14.81, 0]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving only to the right&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[3, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[3, 9.81, 0]&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;State&quot;&gt;Moving up and to the right&lt;/td&gt;
      &lt;td data-th=&quot;Rotation&quot;&gt;[0, 0, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration&quot;&gt;[5, 5, 0]&lt;/td&gt;
      &lt;td data-th=&quot;Acceleration with gravity&quot;&gt;[5, 14.81, 0]&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;sample-calculating-the-maximum-acceleration-of-an-object&quot;&gt;Sample: Calculating the maximum acceleration of an object &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#sample-calculating-the-maximum-acceleration-of-an-object&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;One way to use device motion events is to calculate the maximum acceleration
of an object. For example, what&#39;s the maximum acceleration of a person
jumping?&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;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&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;  jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;x&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&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;  jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;y&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;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z&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;  jumpMax&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; evt&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;acceleration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;z&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;After tapping the Go! button, the user is told to jump. During that time,
the page stores the maximum (and minimum) acceleration values, and after the
jump, tells the user their maximum acceleration.&lt;/p&gt;
&lt;h2 id=&quot;feedback&quot;&gt;Feedback &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/device-orientation/#feedback&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Responsive images</title>
    <link href="https://web.dev/responsive-images/"/>
    <updated>2014-04-29T00:00:00Z</updated>
    <id>https://web.dev/responsive-images/</id>
    <content type="html" mode="escaped">&lt;p&gt;Responsive web design means that not only can our layouts change based on device
characteristics, but content can change as well.  For example, on high
resolution (2x) displays, high resolution graphics ensure sharpness. An image
that  is 50% width may work just fine when the browser is 800px wide, but uses
too much real estate on a narrow phone, and requires the same bandwidth overhead
when scaled down to fit a smaller screen.&lt;/p&gt;
&lt;h2 id=&quot;art-direction&quot;&gt;Art direction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#art-direction&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;figure&gt;
&lt;img alt=&quot;Art direction example&quot; decoding=&quot;async&quot; height=&quot;328&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;Other times the image may need to be changed more drastically: changing the
proportions, cropping, and even replacing the entire image.  In this case,
changing the image is usually referred to as art direction.  See
&lt;a href=&quot;https://responsiveimages.org/demos/&quot; rel=&quot;noopener&quot;&gt;responsiveimages.org/demos/&lt;/a&gt; for more
examples.&lt;/p&gt;
&lt;h2 id=&quot;responsive-images&quot;&gt;Responsive Images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#responsive-images&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
 &lt;a href=&quot;https://www.udacity.com/course/responsive-images--ud882&quot;&gt;
   &lt;figure&gt;
   &lt;img alt=&quot;Udacity Course Screenshot&quot; class=&quot;float-right&quot; decoding=&quot;async&quot; height=&quot;220&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 360px) 360px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/TpGoiZpVMoyjWi0BBJb7.png?auto=format&amp;w=720 720w&quot; width=&quot;360&quot; /&gt;
   &lt;/figure&gt;
&lt;/a&gt;
&lt;p&gt;Did you know that images account for more than 60% of the bytes on average needed to load a web page?&lt;/p&gt;
&lt;p&gt;In this course you will learn how to work with images on the modern web, so
that your images look great and load quickly on any device.&lt;/p&gt;
&lt;p&gt;Along the way, you will pick up a range of skills and techniques to smoothly
integrate responsive images into your development workflow. By the end of
the course, you will be developing with images that adapt and respond to
different viewport sizes and usage scenarios.&lt;/p&gt;
&lt;p&gt;This is a free course offered through &lt;a href=&quot;https://www.udacity.com/&quot; rel=&quot;noopener&quot;&gt;Udacity&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.udacity.com/course/responsive-images--ud882&quot; rel=&quot;noopener&quot;&gt;Take Course&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;images-in-markup&quot;&gt;Images in markup &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#images-in-markup&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;img&lt;/code&gt; element is powerful—it downloads, decodes, and renders
content—and modern browsers support a range of image  formats.  Including
images that work across devices is no different than for desktop, and only
requires a few minor tweaks to create a good experience.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#summary&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use relative sizes for images to prevent them from accidentally overflowing
the container.&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;picture&lt;/code&gt; element when you want to specify different images depending
on device characteristics (a.k.a. art direction).&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;srcset&lt;/code&gt; and the &lt;code&gt;x&lt;/code&gt; descriptor in the  &lt;code&gt;img&lt;/code&gt; element to give hints to
the browser about the best  image to use when choosing from different densities.&lt;/li&gt;
&lt;li&gt;If your page only has one or two images and these are not used elsewhere on
your site, consider using inline images to reduce file requests.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-relative-sizes-for-images&quot;&gt;Use relative sizes for images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-relative-sizes-for-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Remember to use relative units when specifying widths for images to prevent them
from accidentally overflowing the viewport.  For example, &lt;code&gt;width: 50%;&lt;/code&gt;  causes
the image width to be 50% of the containing element (not 50% of the viewport or
50% of actual pixel size).&lt;/p&gt;
&lt;p&gt;Because CSS allows content to overflow its container, you may need to use max-
width: 100% to prevent images and other content from overflowing.  For example:&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 selector&quot;&gt;img, embed, object, video&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;max-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;Be sure to provide meaningful descriptions via the &lt;code&gt;alt&lt;/code&gt; attribute on &lt;code&gt;img&lt;/code&gt;
elements; these help make your site more accessible by giving context to screen
readers and other assistive technologies.&lt;/p&gt;
&lt;h3 id=&quot;enhance-imgs-with-srcset-for-high-dpi-devices&quot;&gt;Enhance &lt;code&gt;img&lt;/code&gt;s with &lt;code&gt;srcset&lt;/code&gt; for high DPI devices &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#enhance-imgs-with-srcset-for-high-dpi-devices&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;Pzc5Dly_jEM&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;srcset&lt;/code&gt; attribute enhances the behavior of the
&lt;code&gt;img&lt;/code&gt; element, making it easy to provide multiple image files
for different device characteristics. Similar to the &lt;code&gt;image-set&lt;/code&gt;
&lt;a href=&quot;https://web.dev/responsive-images/#use-image-set-to-provide-high-res-images&quot;&gt;CSS function&lt;/a&gt;
native to CSS, &lt;code&gt;srcset&lt;/code&gt; allows the browser to choose the best
image depending on the characteristics of the device, for example using
a 2x image on a 2x display, and potentially in the future, a 1x image on
a 2x device when on a limited bandwidth network.&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;photo.png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;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;photo@2x.png 2x&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&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;On browsers that don&#39;t support &lt;code&gt;srcset&lt;/code&gt;, the browser simply uses the default
image file specified by the &lt;code&gt;src&lt;/code&gt; attribute.  This is why it is important to
always include a 1x image that can be displayed on any device, regardless of
capabilities.  When &lt;code&gt;srcset&lt;/code&gt; is supported, the  comma-separated list of
image/conditions is parsed prior to making any requests, and only the most
appropriate image is downloaded and displayed.&lt;/p&gt;
&lt;p&gt;While the conditions can include everything from pixel density to width and
height, only pixel density is well-supported today.  To balance current
behavior with future features, stick with simply providing the 2x image in
the attribute.&lt;/p&gt;
&lt;h3 id=&quot;art-direction-in-responsive-images-with-picture&quot;&gt;Art direction in responsive images with &lt;code&gt;picture&lt;/code&gt; &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#art-direction-in-responsive-images-with-picture&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt=&quot;Art direction example&quot; decoding=&quot;async&quot; height=&quot;328&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 600px) 600px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/cQKbjztkJ45DQoIw3sNH.png?auto=format&amp;w=1200 1200w&quot; width=&quot;600&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;To change images based on device characteristics, also known as art
direction, use the &lt;code&gt;picture&lt;/code&gt; element.  The
&lt;code&gt;picture&lt;/code&gt; element defines a declarative solution for
providing multiple versions of an image based on different
characteristics, like device size, device resolution, orientation,
and more.&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; The &lt;code&gt;picture&lt;/code&gt; element is beginning to land in browsers. Although it&#39;s not available in every browser yet, we recommend its use because of the strong backward compatibility and potential use of the &lt;a href=&quot;https://scottjehl.github.io/picturefill/&quot;&gt;Picturefill polyfill&lt;/a&gt;. See the &lt;a href=&quot;http://responsiveimages.org/#implementation&quot;&gt;ResponsiveImages.org&lt;/a&gt; site for further details. &lt;/div&gt;&lt;/aside&gt;
&lt;div class=&quot;youtube&quot;&gt;  &lt;lite-youtube videoid=&quot;QINlm3vjnaY&quot;&gt;  &lt;/lite-youtube&gt;&lt;/div&gt;
&lt;p&gt;Use the &lt;code&gt;picture&lt;/code&gt; element when an image source
exists in multiple densities, or when a responsive design dictates a
somewhat different image on some types of screens.  Similar to the
&lt;code&gt;video&lt;/code&gt; element, multiple &lt;code&gt;source&lt;/code&gt; elements can
be included, making it possible to specify different image files
depending on media queries or image format.&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;picture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;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;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 800px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;head.jpg, head-2x.jpg 2x&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;source&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;media&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;(min-width: 450px)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;head-small.jpg, head-small-2x.jpg 2x&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;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;head-fb.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;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;head-fb-2x.jpg 2x&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;a head carved out of wood&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;picture&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/media.html&quot; class=&quot;external&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In the above example, if the browser width is at least 800px then either
&lt;code&gt;head.jpg&lt;/code&gt; or &lt;code&gt;head-2x.jpg&lt;/code&gt; is used, depending on the device resolution. If the
browser is between 450px and 800px, then either &lt;code&gt;head-small.jpg&lt;/code&gt; or &lt;code&gt;head-small- 2x.jpg&lt;/code&gt; is used, again, depending on the device resolution. For screen widths
less than 450px and backward compatibility where the &lt;code&gt;picture&lt;/code&gt; element isn’t
supported, the browser renders the &lt;code&gt;img&lt;/code&gt; element instead, and should always be
included.&lt;/p&gt;
&lt;h4 id=&quot;relative-sized-images&quot;&gt;Relative sized images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#relative-sized-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;When the final size of the image isn’t known, it can be difficult to specify a
density descriptor for the image sources.  This is especially true for images
that span a proportional width of the browser and are fluid, depending on the
size of the browser.&lt;/p&gt;
&lt;p&gt;Instead of supplying fixed image sizes and densities, you can specify the size
of each supplied image by adding a width descriptor along with the size of the
image element, allowing the browser to automatically calculate the effective
pixel density and choose the best image to download.&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lighthouse-200.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;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;50vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lighthouse-100.jpg 100w, lighthouse-200.jpg 200w,&lt;br /&gt;             lighthouse-400.jpg 400w, lighthouse-800.jpg 800w,&lt;br /&gt;             lighthouse-1000.jpg 1000w, lighthouse-1400.jpg 1400w,&lt;br /&gt;             lighthouse-1800.jpg 1800w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;a lighthouse&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/sizes.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above example renders an image that is half the viewport width
(&lt;code&gt;sizes=&amp;quot;50vw&amp;quot;&lt;/code&gt;), and depending on the width of the browser and its device pixel
ratio, allows the browser to choose the correct image regardless of how large
the browser window is. For example, the table below shows which image the
browser would choose:&lt;/p&gt;
&lt;table class=&quot;&quot;&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th data-th=&quot;Browser width&quot;&gt;Browser width&lt;/th&gt;
      &lt;th data-th=&quot;Device pixel ratio&quot;&gt;Device pixel ratio&lt;/th&gt;
      &lt;th data-th=&quot;Image used&quot;&gt;Image used&lt;/th&gt;
      &lt;th data-th=&quot;Effective resolution&quot;&gt;Effective resolution&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;400px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;1&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;200.jpg&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;1x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;400px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;2&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;400.jpg&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;2x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;320px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;2&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;400.jpg&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;2.5x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;600px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;2&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;800.jpg&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;2.67x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;640px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;3&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;1000.jpg&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;3.125x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td data-th=&quot;Browser width&quot;&gt;1100px&lt;/td&gt;
      &lt;td data-th=&quot;Device pixel ratio&quot;&gt;1&lt;/td&gt;
      &lt;td data-th=&quot;Image used&quot;&gt;&lt;code&gt;800.png&lt;/code&gt;&lt;/td&gt;
      &lt;td data-th=&quot;Effective resolution&quot;&gt;1.45x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;account-for-breakpoints-in-responsive-images&quot;&gt;Account for breakpoints in responsive images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#account-for-breakpoints-in-responsive-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In many cases, the image size may change depending on the site’s layout
breakpoints.  For example, on a small screen, you might want the image to
span the full width of the viewport, while on larger screens, it should only
take a small proportion.&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;400.png&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;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;(min-width: 600px) 25vw, (min-width: 500px) 50vw, 100vw&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;     &lt;span class=&quot;token attr-name&quot;&gt;srcset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;100.png 100w, 200.png 200w, 400.png 400w,&lt;br /&gt;             800.png 800w, 1600.png 1600w, 2000.png 2000w&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;an example image&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/breakpoints.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;sizes&lt;/code&gt; attribute, in the above example, uses several media queries to
specify the size of the image. When the browser width is greater than
600px, the image is 25% of the viewport width; when it is between 500px
and 600px, the image is 50% of the viewport width; and below 500px, it
is full width.&lt;/p&gt;
&lt;h3 id=&quot;make-product-images-expandable&quot;&gt;Make product images expandable &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#make-product-images-expandable&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt=&quot;J. Crews website with expandable product image&quot; class=&quot;float-right&quot; decoding=&quot;async&quot; height=&quot;550&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 325px) 325px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/hF9ing9oEb4opUianjqU.png?auto=format&amp;w=650 650w&quot; width=&quot;325&quot; /&gt;
  &lt;figcaption&gt;
    J. Crew&#39;s website with expandable product image.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Customers want to see what they&#39;re buying.  On retail sites, users expect to be
able to view high resolution closeups of products to get a better look at
details, and
&lt;a href=&quot;https://developers.google.com/web/fundamentals/getting-started/principles/#make-product-images-expandable&quot; rel=&quot;noopener&quot;&gt;study participants&lt;/a&gt;
got frustrated if they weren&#39;t able to.&lt;/p&gt;
&lt;p&gt;A good example of tappable, expandable images is provided by the J. Crew site.
A disappearing overlay indicates that an image is tappable, providing a zoomed
in image with fine detail visible.&lt;/p&gt;
&lt;h3 id=&quot;other-image-techniques&quot;&gt;Other image techniques &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#other-image-techniques&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;h4 id=&quot;compressive-images&quot;&gt;Compressive images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#compressive-images&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The
&lt;a href=&quot;http://www.html5rocks.com/en/mobile/high-dpi/#toc-tech-overview&quot; rel=&quot;noopener&quot;&gt;compressive image technique&lt;/a&gt;
serves a highly compressed 2x image to all devices, no matter the actual
capabilities of the device.  Depending on the type of image and level of
compression, image quality may not appear to change, but the file size drops
significantly.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/compressive.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;aside class=&quot;aside flow bg-state-bad-bg color-state-bad-text&quot;&gt;&lt;p class=&quot;cluster color-state-bad-text&quot;&gt;&lt;span class=&quot;aside__icon box-block &quot;&gt;&lt;svg width=&quot;24&quot; height=&quot;24&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;currentColor&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; role=&quot;img&quot; aria-label=&quot;Error sign&quot;&gt;   &lt;path fill-rule=&quot;evenodd&quot; clip-rule=&quot;evenodd&quot; d=&quot;M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15v-2h2v2h-2zm0-10v6h2V7h-2z&quot;&gt;&lt;/path&gt; &lt;/svg&gt;&lt;/span&gt;&lt;strong&gt;Caution&lt;/strong&gt;&lt;/p&gt;&lt;div class=&quot; flow&quot;&gt; Use caution with the compressive technique because of the increased memory and decoding costs it requires. Resizing large images to fit on smaller screens is expensive and can be particularly painful on low-end devices where both memory and processing is limited. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;javascript-image-replacement&quot;&gt;JavaScript image replacement &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#javascript-image-replacement&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;JavaScript image replacement checks the capabilities of the device and &amp;quot;does the
right thing.&amp;quot; You can determine device pixel ratio via
&lt;code&gt;window.devicePixelRatio&lt;/code&gt;, get screen width and height, and even potentially do
some network connection sniffing via &lt;code&gt;navigator.connection&lt;/code&gt; or issuing a fake
request. When you&#39;ve collected all of this information, you can decide which
image to load.&lt;/p&gt;
&lt;p&gt;One big drawback to this approach is that using JavaScript means that you will
delay image loading until at least the look-ahead parser has finished. This
means that images won&#39;t even start downloading until after the &lt;code&gt;pageload&lt;/code&gt; event
fires. In addition, the browser will most likely download both the 1x and 2x
images, resulting in increased page weight.&lt;/p&gt;
&lt;h4 id=&quot;inlining-images-raster-and-vector&quot;&gt;Inlining images: raster and vector &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#inlining-images-raster-and-vector&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;There are two fundamentally different ways to create and store images—and
this affects how you deploy images responsively.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Raster images&lt;/strong&gt; — such as photographs and other images, are
represented as a grid of individual dots of color. Raster images might come
from a camera or scanner, or be created with the HTML canvas element. Formats
like PNG, JPEG, and WebP are used to store raster images.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vector images&lt;/strong&gt; such as logos and line art are defined as a set of
curves, lines, shapes, fill colors and gradients. Vector images can be created
with programs like Adobe Illustrator or Inkscape, or handwritten in code using
a vector format such as SVG.&lt;/p&gt;
&lt;h5 id=&quot;svg&quot;&gt;SVG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#svg&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;SVG makes it possible to include responsive vector graphics in a web page. The
advantage of vector file formats over raster file formats is that the browser
can render a vector image at any size. Vector formats describe the geometry of
the image—how it&#39;s constructed from lines, curves, and colors and so on.
Raster formats, on the other hand, only have information about individual dots
of color, so the browser has to guess how to fill in the blanks when scaling.&lt;/p&gt;
&lt;p&gt;Below are two versions of the same image: a PNG image on the left and an SVG on
the right. The SVG looks great at any size, whereas the PNG next to it starts to
look blurry at larger display sizes.&lt;/p&gt;
&lt;div class=&quot;switcher&quot;&gt;
    &lt;figure&gt;
    &lt;img alt=&quot;HTML5 logo, PNG format&quot; decoding=&quot;async&quot; height=&quot;282&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 200px) 200px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/uNe4OQfREZVVpnQOBxiA.png?auto=format&amp;w=400 400w&quot; width=&quot;200&quot; /&gt;
    &lt;/figure&gt;
    &lt;figure&gt;
    &lt;img alt=&quot;HTML5 logo, SVG format&quot; decoding=&quot;async&quot; height=&quot;560&quot; loading=&quot;lazy&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/XxFz3h3CtZrLWwgmiKEy.svg&quot; width=&quot;397&quot; /&gt;
    &lt;/figure&gt;
&lt;/div&gt;
&lt;p&gt;If you want to reduce the number of file requests your page makes, you can code
images inline using SVG or Data URI format. If you view the source of this page,
you&#39;ll see that both logos below are declared inline: a Data URI and an SVG.&lt;/p&gt;
&lt;p&gt;&lt;img class=&quot;side-by-side&quot; src=&quot;data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiB      BZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW      9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RUR      CBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2      ZzExLmR0ZCI+DQo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IkxheWVyXzEiIHhtbG5zPSJodHRwOi8      vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OT      kveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB3aWR0aD0iMzk2Ljc0cHgiIGhlaWdodD0iNTYwc      HgiIHZpZXdCb3g9IjI4MS42MyAwIDM5Ni43NCA1NjAiIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcg      MjgxLjYzIDAgMzk2Ljc0IDU2MCIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSINCgk+DQo8Zz4NCgk8Zz4      NCgkJPGc+DQoJCQk8cG9seWdvbiBmaWxsPSIjRTQ0RDI2IiBwb2ludHM9IjQwOS43MzcsMjQyLj      UwMiA0MTQuMjc2LDI5My4zNjIgNDc5LjgyOCwyOTMuMzYyIDQ4MCwyOTMuMzYyIDQ4MCwyNDIuN      TAyIDQ3OS44MjgsMjQyLjUwMiAJCQkNCgkJCQkiLz4NCgkJCTxwYXRoIGZpbGw9IiNFNDREMjYi      IGQ9Ik0yODEuNjMsMTEwLjA1M2wzNi4xMDYsNDA0Ljk2OEw0NzkuNzU3LDU2MGwxNjIuNDctNDU    uMDQybDM2LjE0NC00MDQuOTA1SDI4MS42M3ogTTYxMS4yODMsNDg5LjE3Ng0KCQkJCUw0ODAsNT    I1LjU3MlY0NzQuMDNsLTAuMjI5LDAuMDYzTDM3OC4wMzEsNDQ1Ljg1bC02Ljk1OC03Ny45ODVoM    jIuOThoMjYuODc5bDMuNTM2LDM5LjYxMmw1NS4zMTUsMTQuOTM3bDAuMDQ2LTAuMDEzdi0wLjAw    NA0KCQkJCUw0ODAsNDIyLjM1di03OS4zMmgtMC4xNzJIMzY4Ljg1M2wtMTIuMjA3LTEzNi44NzF    sLTEuMTg5LTEzLjMyNWgxMjQuMzcxSDQ4MHYtNDkuNjY4aDE2Mi4xN0w2MTEuMjgzLDQ4OS4xNz    Z6Ii8+DQoJCQk8cG9seWdvbiBmaWxsPSIjRjE2NTI5IiBwb2ludHM9IjQ4MCwxOTIuODMzIDYwN    C4yNDcsMTkyLjgzMyA2MDMuMDU5LDIwNi4xNTkgNjAwLjc5NiwyMzEuMzM4IDU5OS44LDI0Mi41    MDIgNTk5LjY0LDI0Mi41MDIgDQoJCQkJNDgwLDI0Mi41MDIgNDgwLDI5My4zNjIgNTgxLjg5Niw    yOTMuMzYyIDU5NS4yOCwyOTMuMzYyIDU5NC4wNjgsMzA2LjY5OSA1ODIuMzk2LDQzNy40NTggNT    gxLjY0OSw0NDUuODUgNDgwLDQ3NC4wMjEgDQoJCQkJNDgwLDQ3NC4wMyA0ODAsNTI1LjU3MiA2M    TEuMjgzLDQ4OS4xNzYgNjQyLjE3LDE0My4xNjYgNDgwLDE0My4xNjYgCQkJIi8+DQoJCQk8cG9s    eWdvbiBmaWxsPSIjRjE2NTI5IiBwb2ludHM9IjU0MC45ODgsMzQzLjAyOSA0ODAsMzQzLjAyOSA    0ODAsNDIyLjM1IDUzNS4yMjQsNDA3LjQ0NSAJCQkiLz4NCgkJCTxwb2x5Z29uIGZpbGw9IiNFQk    VCRUIiIHBvaW50cz0iNDE0LjI3NiwyOTMuMzYyIDQwOS43MzcsMjQyLjUwMiA0NzkuODI4LDI0M    i41MDIgNDc5LjgyOCwyNDIuMzggNDc5LjgyOCwyMjMuNjgyIA0KCQkJCTQ3OS44MjgsMTkyLjgz    MyAzNTUuNDU3LDE5Mi44MzMgMzU2LjY0NiwyMDYuMTU5IDM2OC44NTMsMzQzLjAyOSA0NzkuODI    4LDM0My4wMjkgNDc5LjgyOCwyOTMuMzYyIAkJCSIvPg0KCQkJPHBvbHlnb24gZmlsbD0iI0VCRU    JFQiIgcG9pbnRzPSI0NzkuODI4LDQ3NC4wNjkgNDc5LjgyOCw0MjIuNCA0NzkuNzgyLDQyMi40M    TMgNDI0LjQ2Nyw0MDcuNDc3IDQyMC45MzEsMzY3Ljg2NCANCgkJCQkzOTQuMDUyLDM2Ny44NjQg    MzcxLjA3MiwzNjcuODY0IDM3OC4wMzEsNDQ1Ljg1IDQ3OS43NzEsNDc0LjA5NCA0ODAsNDc0LjA    zIDQ4MCw0NzQuMDIxIAkJCSIvPg0KCQkJPHBvbHlnb24gcG9pbnRzPSIzNDMuNzg0LDUwLjIyOS    AzNjYuODc0LDUwLjIyOSAzNjYuODc0LDc1LjUxNyAzOTIuMTE0LDc1LjUxNyAzOTIuMTE0LDAgM    zY2Ljg3MywwIDM2Ni44NzMsMjQuOTM4IA0KCQkJCTM0My43ODMsMjQuOTM4IDM0My43ODMsMCAz    MTguNTQ0LDAgMzE4LjU0NCw3NS41MTcgMzQzLjc4NCw3NS41MTcgCQkJIi8+DQoJCQk8cG9seWd    vbiBwb2ludHM9IjQyNS4zMDcsMjUuMDQyIDQyNS4zMDcsNzUuNTE3IDQ1MC41NDksNzUuNTE3ID    Q1MC41NDksMjUuMDQyIDQ3Mi43NzksMjUuMDQyIDQ3Mi43NzksMCA0MDMuMDg1LDAgDQoJCQkJN    DAzLjA4NSwyNS4wNDIgNDI1LjMwNiwyNS4wNDIgCQkJIi8+DQoJCQk8cG9seWdvbiBwb2ludHM9    IjUwOC41MzcsMzguMDg2IDUyNS45MTQsNjQuOTM3IDUyNi4zNDksNjQuOTM3IDU0My43MTQsMzg    uMDg2IDU0My43MTQsNzUuNTE3IDU2OC44NTEsNzUuNTE3IDU2OC44NTEsMCANCgkJCQk1NDIuNT    IyLDAgNTI2LjM0OSwyNi41MzQgNTEwLjE1OSwwIDQ4My44NCwwIDQ4My44NCw3NS41MTcgNTA4L    jUzNyw3NS41MTcgCQkJIi8+DQoJCQk8cG9seWdvbiBwb2ludHM9IjY0Mi4xNTYsNTAuNTU1IDYw    Ni42Niw1MC41NTUgNjA2LjY2LDAgNTgxLjQxMiwwIDU4MS40MTIsNzUuNTE3IDY0Mi4xNTYsNzU    uNTE3IAkJCSIvPg0KCQkJPHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgcG9pbnRzPSI0ODAsNDc0Lj    AyMSA1ODEuNjQ5LDQ0NS44NSA1ODIuMzk2LDQzNy40NTggNTk0LjA2OCwzMDYuNjk5IDU5NS4yO    CwyOTMuMzYyIDU4MS44OTYsMjkzLjM2MiANCgkJCQk0ODAsMjkzLjM2MiA0NzkuODI4LDI5My4z    NjIgNDc5LjgyOCwzNDMuMDI5IDQ4MCwzNDMuMDI5IDU0MC45ODgsMzQzLjAyOSA1MzUuMjI0LDQ    wNy40NDUgNDgwLDQyMi4zNSA0NzkuODI4LDQyMi4zOTYgDQoJCQkJNDc5LjgyOCw0MjIuNCA0Nz    kuODI4LDQ3NC4wNjkgCQkJIi8+DQoJCQk8cG9seWdvbiBmaWxsPSIjRkZGRkZGIiBwb2ludHM9I    jQ3OS44MjgsMjQyLjM4IDQ3OS44MjgsMjQyLjUwMiA0ODAsMjQyLjUwMiA1OTkuNjQsMjQyLjUw    MiA1OTkuOCwyNDIuNTAyIDYwMC43OTYsMjMxLjMzOCANCgkJCQk2MDMuMDU5LDIwNi4xNTkgNjA    0LjI0NywxOTIuODMzIDQ4MCwxOTIuODMzIDQ3OS44MjgsMTkyLjgzMyA0NzkuODI4LDIyMy42OD    IgCQkJIi8+DQoJCTwvZz4NCgk8L2c+DQo8L2c+DQo8L3N2Zz4NCg==&quot; /&gt;
&lt;svg class=&quot;side-by-side&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; x=&quot;0px&quot; y=&quot;0px&quot; width=&quot;396.74px&quot; height=&quot;560px&quot; viewBox=&quot;281.63 0 396.74 560&quot; enable-background=&quot;new 281.63 0 396.74 560&quot; xml:space=&quot;preserve&quot;&gt;&lt;g&gt;&lt;g&gt;&lt;g&gt;&lt;polygon fill=&quot;#E44D26&quot; points=&quot;409.737,242.502 414.276,293.362 479.828,293.362 480,293.362 480,242.502 479.828,242.502&quot;&gt;&lt;/polygon&gt;&lt;path fill=&quot;#E44D26&quot; d=&quot;M281.63,110.053l36.106,404.968L479.757,560l162.47-45.042l36.144-404.905H281.63z M611.283,489.176 L480,525.572V474.03l-0.229,0.063L378.031,445.85l-6.958-77.985h22.98h26.879l3.536,39.612l55.315,14.937l0.046-0.013v-0.004 L480,422.35v-79.32h-0.172H368.853l-12.207-136.871l-1.189-13.325h124.371H480v-49.668h162.17L611.283,489.176z&quot;&gt;&lt;/path&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;480,192.833 604.247,192.833 603.059,206.159 600.796,231.338 599.8,242.502 599.64,242.502 480,242.502 480,293.362 581.896,293.362 595.28,293.362 594.068,306.699 582.396,437.458 581.649,445.85 480,474.021 480,474.03 480,525.572 611.283,489.176 642.17,143.166 480,143.166       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;540.988,343.029 480,343.029 480,422.35 535.224,407.445      &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;414.276,293.362 409.737,242.502 479.828,242.502 479.828,242.38 479.828,223.682 479.828,192.833 355.457,192.833 356.646,206.159 368.853,343.029 479.828,343.029 479.828,293.362       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;479.828,474.069 479.828,422.4 479.782,422.413 424.467,407.477 420.931,367.864 394.052,367.864 371.072,367.864 378.031,445.85 479.771,474.094 480,474.03 480,474.021       &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;343.784,50.229 366.874,50.229 366.874,75.517 392.114,75.517 392.114,0 366.873,0 366.873,24.938 343.783,24.938 343.783,0 318.544,0 318.544,75.517 343.784,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;425.307,25.042 425.307,75.517 450.549,75.517 450.549,25.042 472.779,25.042 472.779,0 403.085,0 403.085,25.042 425.306,25.042       &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;508.537,38.086 525.914,64.937 526.349,64.937 543.714,38.086 543.714,75.517 568.851,75.517 568.851,0 542.522,0 526.349,26.534 510.159,0 483.84,0 483.84,75.517 508.537,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;642.156,50.555 606.66,50.555 606.66,0 581.412,0 581.412,75.517 642.156,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;480,474.021 581.649,445.85 582.396,437.458 594.068,306.699 595.28,293.362 581.896,293.362 480,293.362 479.828,293.362 479.828,343.029 480,343.029 540.988,343.029 535.224,407.445 480,422.35 479.828,422.396 479.828,422.4 479.828,474.069       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;479.828,242.38 479.828,242.502 480,242.502 599.64,242.502 599.8,242.502 600.796,231.338 603.059,206.159 604.247,192.833 480,192.833 479.828,192.833 479.828,223.682       &quot;&gt;&lt;/polygon&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;p&gt;SVG has &lt;a href=&quot;http://caniuse.com/svg-html5&quot; rel=&quot;noopener&quot;&gt;great support&lt;/a&gt; on mobile and desktop,
and &lt;a href=&quot;https://sarasoueidan.com/blog/svgo-tools/&quot; rel=&quot;noopener&quot;&gt;optimization tools&lt;/a&gt; can
significantly reduce SVG size. The following two inline SVG logos look
identical, but one is around 3KB and the other only 2KB:&lt;/p&gt;
&lt;p&gt;&lt;svg class=&quot;side-by-side&quot; version=&quot;1.1&quot; id=&quot;Layer_1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; x=&quot;0px&quot; y=&quot;0px&quot; width=&quot;396.74px&quot; height=&quot;560px&quot; viewBox=&quot;281.63 0 396.74 560&quot; enable-background=&quot;new 281.63 0 396.74 560&quot; xml:space=&quot;preserve&quot;&gt;&lt;g&gt;&lt;g&gt;&lt;g&gt;&lt;polygon fill=&quot;#E44D26&quot; points=&quot;409.737,242.502 414.276,293.362 479.828,293.362 480,293.362 480,242.502 479.828,242.502&quot;&gt;&lt;/polygon&gt;&lt;path fill=&quot;#E44D26&quot; d=&quot;M281.63,110.053l36.106,404.968L479.757,560l162.47-45.042l36.144-404.905H281.63z M611.283,489.176 L480,525.572V474.03l-0.229,0.063L378.031,445.85l-6.958-77.985h22.98h26.879l3.536,39.612l55.315,14.937l0.046-0.013v-0.004 L480,422.35v-79.32h-0.172H368.853l-12.207-136.871l-1.189-13.325h124.371H480v-49.668h162.17L611.283,489.176z&quot;&gt;&lt;/path&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;480,192.833 604.247,192.833 603.059,206.159 600.796,231.338 599.8,242.502 599.64,242.502 480,242.502 480,293.362 581.896,293.362 595.28,293.362 594.068,306.699 582.396,437.458 581.649,445.85 480,474.021 480,474.03 480,525.572 611.283,489.176 642.17,143.166 480,143.166       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;540.988,343.029 480,343.029 480,422.35 535.224,407.445      &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;414.276,293.362 409.737,242.502 479.828,242.502 479.828,242.38 479.828,223.682 479.828,192.833 355.457,192.833 356.646,206.159 368.853,343.029 479.828,343.029 479.828,293.362       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;479.828,474.069 479.828,422.4 479.782,422.413 424.467,407.477 420.931,367.864 394.052,367.864 371.072,367.864 378.031,445.85 479.771,474.094 480,474.03 480,474.021       &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;343.784,50.229 366.874,50.229 366.874,75.517 392.114,75.517 392.114,0 366.873,0 366.873,24.938 343.783,24.938 343.783,0 318.544,0 318.544,75.517 343.784,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;425.307,25.042 425.307,75.517 450.549,75.517 450.549,25.042 472.779,25.042 472.779,0 403.085,0 403.085,25.042 425.306,25.042       &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;508.537,38.086 525.914,64.937 526.349,64.937 543.714,38.086 543.714,75.517 568.851,75.517 568.851,0 542.522,0 526.349,26.534 510.159,0 483.84,0 483.84,75.517 508.537,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;642.156,50.555 606.66,50.555 606.66,0 581.412,0 581.412,75.517 642.156,75.517      &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;480,474.021 581.649,445.85 582.396,437.458 594.068,306.699 595.28,293.362 581.896,293.362 480,293.362 479.828,293.362 479.828,343.029 480,343.029 540.988,343.029 535.224,407.445 480,422.35 479.828,422.396 479.828,422.4 479.828,474.069       &quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;479.828,242.38 479.828,242.502 480,242.502 599.64,242.502 599.8,242.502 600.796,231.338 603.059,206.159 604.247,192.833 480,192.833 479.828,192.833 479.828,223.682       &quot;&gt;&lt;/polygon&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;svg class=&quot;side-by-side&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot; version=&quot;1.1&quot; x=&quot;0px&quot; y=&quot;0px&quot; width=&quot;50%&quot; height=&quot;560px&quot; viewBox=&quot;281.63 0 396.74 560&quot; enable-background=&quot;new 281.63 0 396.74 560&quot; xml:space=&quot;preserve&quot;&gt;&lt;g&gt;&lt;g&gt;&lt;g&gt;&lt;polygon fill=&quot;#E44D26&quot; points=&quot;409.7,242.5 414.3,293.4 479.8,293.4 480,293.4 480,242.5 479.8,242.5&quot;&gt;&lt;/polygon&gt;&lt;path fill=&quot;#E44D26&quot; d=&quot;M281.63 110.053l36.106 404.968L479.757 560l162.47-45.042l36.144-404.905H281.63z M611.283 489.2 L480 525.572V474.03l-0.229 0.063L378.031 445.85l-6.958-77.985h22.98h26.879l3.536 39.612l55.315 14.937l0.046-0.013v-0.004 L480 422.35v-79.32h-0.172H368.853l-12.207-136.871l-1.189-13.325h124.371H480v-49.668h162.17L611.283 489.176z&quot;&gt;&lt;/path&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;480,192.8 604.2,192.8 603.1,206.2 600.8,231.3 599.8,242.5 599.6,242.5 480,242.5 480,293.4 581.9,293.4 595.3,293.4 594.1,306.7 582.4,437.5 581.6,445.9 480,474 480,474 480,525.6 611.3,489.2 642.2,143.2 480,143.2&quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#F16529&quot; points=&quot;541,343 480,343 480,422.4 535.2,407.4&quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;414.3,293.4 409.7,242.5 479.8,242.5 479.8,242.4 479.8,223.7 479.8,192.8 355.5,192.8 356.6,206.2 368.9,343 479.8,343 479.8,293.4&quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#EBEBEB&quot; points=&quot;479.8,474.1 479.8,422.4 479.8,422.4 424.5,407.5 420.9,367.9 394.1,367.9 371.1,367.9 378,445.9 479.8,474.1 480,474 480,474&quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;343.8,50.2 366.9,50.2 366.9,75.5 392.1,75.5 392.1,0 366.9,0 366.9,24.9 343.8,24.9 343.8,0 318.5,0 318.5,75.5 343.8,75.5&quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;425.3,25 425.3,75.5 450.5,75.5 450.5,25 472.8,25 472.8,0 403.1,0 403.1,25 425.3,25&quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;508.5,38.1 525.9,64.9 526.3,64.9 543.7,38.1 543.7,75.5 568.9,75.5 568.9,0 542.5,0 526.3,26.5 510.2,0 483.8,0 483.8,75.5 508.5,75.5&quot;&gt;&lt;/polygon&gt;&lt;polygon points=&quot;642.2,50.6 606.7,50.6 606.7,0 581.4,0 581.4,75.5 642.2,75.5&quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;480,474 581.6,445.9 582.4,437.5 594.1,306.7 595.3,293.4 581.9,293.4 480,293.4 479.8,293.4 479.8,343 480,343 541,343 535.2,407.4 480,422.4 479.8,422.4 479.8,422.4 479.8,474.1&quot;&gt;&lt;/polygon&gt;&lt;polygon fill=&quot;#FFFFFF&quot; points=&quot;479.8,242.4 479.8,242.5 480,242.5 599.6,242.5 599.8,242.5 600.8,231.3 603.1,206.2 604.2,192.8 480,192.8 479.8,192.8 479.8,223.7&quot;&gt;&lt;/polygon&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/p&gt;
&lt;h5 id=&quot;data-uri&quot;&gt;Data URI &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#data-uri&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Data URIs provide a way to include a file, such as an image, inline by setting
the src of an &lt;code&gt;img&lt;/code&gt; element as a Base64 encoded string using the
following format:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;data:image/svg+xml;base64,[data]&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;The start of the code for the HTML5 logo above looks like this:&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 tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiB&lt;br /&gt;BZG9iZSBJbGx1c3RyYXRvciAxNi4wLjAsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW ...&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;(The full version is over 5000 characters in length!)&lt;/p&gt;
&lt;p&gt;Drag &#39;n&#39; drop tool such as
&lt;a href=&quot;https://jpillora.com/base64-encoder&quot; rel=&quot;noopener&quot;&gt;jpillora.com/base64-encoder&lt;/a&gt; are
available to convert binary files such as images to Data URIs. Just like SVGs,
Data URIs are &lt;a href=&quot;http://caniuse.com/datauri&quot; rel=&quot;noopener&quot;&gt;well supported&lt;/a&gt; on mobile and
desktop browsers.&lt;/p&gt;
&lt;h5 id=&quot;inlining-in-css&quot;&gt;Inlining in CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#inlining-in-css&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Data URIs and SVGs can also be inlined in CSS—and this is supported on
both mobile and desktop. Here are two identical-looking images implemented as
background images in CSS; one Data URI, one SVG:&lt;/p&gt;
&lt;style&gt;
  .side-by-side {
    display: inline-block;
    margin: 0 20px 0 0;
    width: 45%;
  }

  span#data_uri {
    background: url(data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%3E%0D%0A%3C%21--%20Generator%3A%20Adobe%20Illustrator%2016.0.0%2C%20SVG%20Export%20Plug-In%20.%20SVG%20Version%3A%206.00%20Build%200%29%20%20--%3E%0D%0A%3C%21DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%0D%0A%3Csvg%20version%3D%221.1%22%20id%3D%22Layer_1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%0D%0A%09%20width%3D%22396.74px%22%20height%3D%22560px%22%20viewBox%3D%22281.63%200%20396.74%20560%22%20enable-background%3D%22new%20281.63%200%20396.74%20560%22%20xml%3Aspace%3D%22preserve%22%0D%0A%09%3E%0D%0A%3Cg%3E%0D%0A%09%3Cg%3E%0D%0A%09%09%3Cg%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23E44D26%22%20points%3D%22409.737%2C242.502%20414.276%2C293.362%20479.828%2C293.362%20480%2C293.362%20480%2C242.502%20479.828%2C242.502%20%09%09%09%0D%0A%09%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpath%20fill%3D%22%23E44D26%22%20d%3D%22M281.63%2C110.053l36.106%2C404.968L479.757%2C560l162.47-45.042l36.144-404.905H281.63z%20M611.283%2C489.176%0D%0A%09%09%09%09L480%2C525.572V474.03l-0.229%2C0.063L378.031%2C445.85l-6.958-77.985h22.98h26.879l3.536%2C39.612l55.315%2C14.937l0.046-0.013v-0.004%0D%0A%09%09%09%09L480%2C422.35v-79.32h-0.172H368.853l-12.207-136.871l-1.189-13.325h124.371H480v-49.668h162.17L611.283%2C489.176z%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23F16529%22%20points%3D%22480%2C192.833%20604.247%2C192.833%20603.059%2C206.159%20600.796%2C231.338%20599.8%2C242.502%20599.64%2C242.502%20%0D%0A%09%09%09%09480%2C242.502%20480%2C293.362%20581.896%2C293.362%20595.28%2C293.362%20594.068%2C306.699%20582.396%2C437.458%20581.649%2C445.85%20480%2C474.021%20%0D%0A%09%09%09%09480%2C474.03%20480%2C525.572%20611.283%2C489.176%20642.17%2C143.166%20480%2C143.166%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23F16529%22%20points%3D%22540.988%2C343.029%20480%2C343.029%20480%2C422.35%20535.224%2C407.445%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23EBEBEB%22%20points%3D%22414.276%2C293.362%20409.737%2C242.502%20479.828%2C242.502%20479.828%2C242.38%20479.828%2C223.682%20%0D%0A%09%09%09%09479.828%2C192.833%20355.457%2C192.833%20356.646%2C206.159%20368.853%2C343.029%20479.828%2C343.029%20479.828%2C293.362%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23EBEBEB%22%20points%3D%22479.828%2C474.069%20479.828%2C422.4%20479.782%2C422.413%20424.467%2C407.477%20420.931%2C367.864%20%0D%0A%09%09%09%09394.052%2C367.864%20371.072%2C367.864%20378.031%2C445.85%20479.771%2C474.094%20480%2C474.03%20480%2C474.021%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20points%3D%22343.784%2C50.229%20366.874%2C50.229%20366.874%2C75.517%20392.114%2C75.517%20392.114%2C0%20366.873%2C0%20366.873%2C24.938%20%0D%0A%09%09%09%09343.783%2C24.938%20343.783%2C0%20318.544%2C0%20318.544%2C75.517%20343.784%2C75.517%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20points%3D%22425.307%2C25.042%20425.307%2C75.517%20450.549%2C75.517%20450.549%2C25.042%20472.779%2C25.042%20472.779%2C0%20403.085%2C0%20%0D%0A%09%09%09%09403.085%2C25.042%20425.306%2C25.042%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20points%3D%22508.537%2C38.086%20525.914%2C64.937%20526.349%2C64.937%20543.714%2C38.086%20543.714%2C75.517%20568.851%2C75.517%20568.851%2C0%20%0D%0A%09%09%09%09542.522%2C0%20526.349%2C26.534%20510.159%2C0%20483.84%2C0%20483.84%2C75.517%20508.537%2C75.517%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20points%3D%22642.156%2C50.555%20606.66%2C50.555%20606.66%2C0%20581.412%2C0%20581.412%2C75.517%20642.156%2C75.517%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23FFFFFF%22%20points%3D%22480%2C474.021%20581.649%2C445.85%20582.396%2C437.458%20594.068%2C306.699%20595.28%2C293.362%20581.896%2C293.362%20%0D%0A%09%09%09%09480%2C293.362%20479.828%2C293.362%20479.828%2C343.029%20480%2C343.029%20540.988%2C343.029%20535.224%2C407.445%20480%2C422.35%20479.828%2C422.396%20%0D%0A%09%09%09%09479.828%2C422.4%20479.828%2C474.069%20%09%09%09%22%2F%3E%0D%0A%09%09%09%3Cpolygon%20fill%3D%22%23FFFFFF%22%20points%3D%22479.828%2C242.38%20479.828%2C242.502%20480%2C242.502%20599.64%2C242.502%20599.8%2C242.502%20600.796%2C231.338%20%0D%0A%09%09%09%09603.059%2C206.159%20604.247%2C192.833%20480%2C192.833%20479.828%2C192.833%20479.828%2C223.682%20%09%09%09%22%2F%3E%0D%0A%09%09%3C%2Fg%3E%0D%0A%09%3C%2Fg%3E%0D%0A%3C%2Fg%3E%0D%0A%3C%2Fsvg%3E%0D%0A) no-repeat;
    background-size: cover;
    height: 484px;
  }

  span#svg {
    background: url(&quot;data:image/svg+xml;utf8,&lt;svg xmlns=&#39;http://www.w3.org/2000/svg&#39; xmlns:xlink=&#39;http://www.w3.org/1999/xlink&#39; version=&#39;1.1&#39; x=&#39;0px&#39; y=&#39;0px&#39; width=&#39;50%&#39; height=&#39;560px&#39; viewBox=&#39;281.63 0 396.74 560&#39; enable-background=&#39;new 281.63 0 396.74 560&#39; xml:space=&#39;preserve&#39;&gt;&lt;g&gt;&lt;g&gt;&lt;g&gt;&lt;polygon fill=&#39;#E44D26&#39; points=&#39;409.7,242.5 414.3,293.4 479.8,293.4 480,293.4 480,242.5 479.8,242.5&#39;/&gt;&lt;path fill=&#39;#E44D26&#39; d=&#39;M281.63 110.053l36.106 404.968L479.757 560l162.47-45.042l36.144-404.905H281.63z M611.283 489.2 L480 525.572V474.03l-0.229 0.063L378.031 445.85l-6.958-77.985h22.98h26.879l3.536 39.612l55.315 14.937l0.046-0.013v-0.004 L480 422.35v-79.32h-0.172H368.853l-12.207-136.871l-1.189-13.325h124.371H480v-49.668h162.17L611.283 489.176z&#39;/&gt;&lt;polygon fill=&#39;#F16529&#39; points=&#39;480,192.8 604.2,192.8 603.1,206.2 600.8,231.3 599.8,242.5 599.6,242.5 480,242.5 480,293.4 581.9,293.4 595.3,293.4 594.1,306.7 582.4,437.5 581.6,445.9 480,474 480,474 480,525.6 611.3,489.2 642.2,143.2 480,143.2&#39;/&gt;&lt;polygon fill=&#39;#F16529&#39; points=&#39;541,343 480,343 480,422.4 535.2,407.4&#39;/&gt;&lt;polygon fill=&#39;#EBEBEB&#39; points=&#39;414.3,293.4 409.7,242.5 479.8,242.5 479.8,242.4 479.8,223.7 479.8,192.8 355.5,192.8 356.6,206.2 368.9,343 479.8,343 479.8,293.4&#39;/&gt;&lt;polygon fill=&#39;#EBEBEB&#39; points=&#39;479.8,474.1 479.8,422.4 479.8,422.4 424.5,407.5 420.9,367.9 394.1,367.9 371.1,367.9 378,445.9 479.8,474.1 480,474 480,474&#39;/&gt;&lt;polygon points=&#39;343.8,50.2 366.9,50.2 366.9,75.5 392.1,75.5 392.1,0 366.9,0 366.9,24.9 343.8,24.9 343.8,0 318.5,0 318.5,75.5 343.8,75.5&#39;/&gt;&lt;polygon points=&#39;425.3,25 425.3,75.5 450.5,75.5 450.5,25 472.8,25 472.8,0 403.1,0 403.1,25 425.3,25&#39;/&gt;&lt;polygon points=&#39;508.5,38.1 525.9,64.9 526.3,64.9 543.7,38.1 543.7,75.5 568.9,75.5 568.9,0 542.5,0 526.3,26.5 510.2,0 483.8,0 483.8,75.5 508.5,75.5&#39;/&gt;&lt;polygon points=&#39;642.2,50.6 606.7,50.6 606.7,0 581.4,0 581.4,75.5 642.2,75.5&#39;/&gt;&lt;polygon fill=&#39;#FFFFFF&#39; points=&#39;480,474 581.6,445.9 582.4,437.5 594.1,306.7 595.3,293.4 581.9,293.4 480,293.4 479.8,293.4 479.8,343 480,343 541,343 535.2,407.4 480,422.4 479.8,422.4 479.8,422.4 479.8,474.1&#39;/&gt;&lt;polygon fill=&#39;#FFFFFF&#39; points=&#39;479.8,242.4 479.8,242.5 480,242.5 599.6,242.5 599.8,242.5 600.8,231.3 603.1,206.2 604.2,192.8 480,192.8 479.8,192.8 479.8,223.7&#39;/&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&quot;) no-repeat;
    background-size: cover;
    height: 484px;
  }
&lt;/style&gt;
&lt;p&gt;&lt;span class=&quot;side-by-side&quot; id=&quot;data_uri&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;side-by-side&quot; id=&quot;svg&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h5 id=&quot;inlining-pros-and-cons&quot;&gt;Inlining pros &amp;amp; cons &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#inlining-pros-and-cons&quot;&gt;#&lt;/a&gt;&lt;/h5&gt;
&lt;p&gt;Inline code for images can be verbose—especially Data URIs—so why
would you want to use it? To reduce HTTP requests! SVGs and Data URIs can enable
an entire web page, including images, CSS and JavaScript, to be retrieved with
one single request.&lt;/p&gt;
&lt;p&gt;On the downside:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On mobile, Data URIs can be significantly slower
to display on mobile than images from an external &lt;code&gt;src&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Data URIs can considerably increase the size of an HTML request.&lt;/li&gt;
&lt;li&gt;They add complexity to your markup and your workflow.&lt;/li&gt;
&lt;li&gt;The Data URI format is considerably bigger than binary (up to 30%) and
therefore doesn&#39;t reduce total download size.&lt;/li&gt;
&lt;li&gt;Data URIs cannot be cached, so must be downloaded for every page they&#39;re used on.&lt;/li&gt;
&lt;li&gt;They&#39;re not supported in IE 6 and 7, incomplete support in IE8.&lt;/li&gt;
&lt;li&gt;With HTTP/2, reducing the number of asset requests will become less of a priority.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As with all things responsive, you need to test what works best. Use developer
tools to measure download file size, the number of requests, and the total
latency. Data URIs can sometimes be useful for raster images—for example,
on a homepage that only has one or two photos that aren&#39;t used elsewhere. If you
need to inline vector images, SVG is a much better option.&lt;/p&gt;
&lt;h2 id=&quot;images-in-css&quot;&gt;Images in CSS &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#images-in-css&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The CSS &lt;code&gt;background&lt;/code&gt; property is a powerful tool for adding complex images
to elements, making it easy to add multiple images, and causing them to repeat,
and more.  When combined with media queries, the background property becomes
even more powerful, enabling conditional image loading based on screen
resolution, viewport size, and more.&lt;/p&gt;
&lt;h3 id=&quot;summary-2&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#summary-2&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use the best image for the characteristics of the display, consider screen
size, device resolution, and page layout.&lt;/li&gt;
&lt;li&gt;Change the &lt;code&gt;background-image&lt;/code&gt; property in CSS for high DPI displays using
media queries with &lt;code&gt;min-resolution&lt;/code&gt; and &lt;code&gt;-webkit-min-device-pixel-ratio&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use srcset to provide high resolution images in addition to the 1x image in
markup.&lt;/li&gt;
&lt;li&gt;Consider the performance costs when using JavaScript image replacement
techniques or when serving highly compressed high resolution images to
lower resolution devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-media-queries-for-conditional-image-loading-or-art-direction&quot;&gt;Use media queries for conditional image loading or art direction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-media-queries-for-conditional-image-loading-or-art-direction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Media queries not only affect the page layout; you can also use them to
conditionally load images or to provide art direction depending on the viewport
width.&lt;/p&gt;
&lt;p&gt;For example, in the sample below, on smaller screens only &lt;code&gt;small.png&lt;/code&gt; is
downloaded and applied to the content &lt;code&gt;div&lt;/code&gt;, while on larger screens
&lt;code&gt;background-image: url(body.png)&lt;/code&gt; is applied to the body and &lt;code&gt;background-image: url(large.png)&lt;/code&gt; is applied to the content &lt;code&gt;div&lt;/code&gt;.&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 selector&quot;&gt;.example&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;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 400px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;small.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no-repeat&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contain&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-position-x&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&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 atrule&quot;&gt;&lt;span class=&quot;token rule&quot;&gt;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 500px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;body.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token selector&quot;&gt;.example&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;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&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;large.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/conditional-mq.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;use-image-set-to-provide-high-res-images&quot;&gt;Use image-set to provide high res images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-image-set-to-provide-high-res-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;image-set()&lt;/code&gt; function in CSS enhances the behavior &lt;code&gt;background&lt;/code&gt; property,
making it easy to provide multiple image files for different device
characteristics.  This allows the browser to choose the best image depending on
the characteristics of the device, for example using a 2x image on a 2x display,
or a 1x image on a 2x device when on a limited bandwidth network.&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 property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;image-set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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;icon1x.jpg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 1x&lt;span class=&quot;token punctuation&quot;&gt;,&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;icon2x.jpg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 2x&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;In addition to loading the correct image, the browser also scales it
accordingly. In other words, the browser assumes that 2x images are twice as
large as 1x images, and so scales the 2x image down by a factor of 2, so
that the image appears to be the same size on the page.&lt;/p&gt;
&lt;p&gt;Support for &lt;code&gt;image-set()&lt;/code&gt; is still new and is only supported in Chrome and
Safari with the &lt;code&gt;-webkit&lt;/code&gt; vendor prefix.  Take care to include a
fallback image for when &lt;code&gt;image-set()&lt;/code&gt; is not supported; for example:&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 selector&quot;&gt;.sample&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 128px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 128px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;icon1x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;-webkit-image-set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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;icon1x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 1x&lt;span class=&quot;token punctuation&quot;&gt;,&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;icon2x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 2x&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 property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;image-set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&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;icon1x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 1x&lt;span class=&quot;token punctuation&quot;&gt;,&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;icon2x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; 2x&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/image-set.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The above loads the appropriate asset in browsers that support image-set;
otherwise it falls back to the 1x asset. The obvious caveat is that while
&lt;code&gt;image-set()&lt;/code&gt; browser support is low, most browsers get the 1x asset.&lt;/p&gt;
&lt;h3 id=&quot;use-media-queries-to-provide-high-res-images-or-art-direction&quot;&gt;Use media queries to provide high res images or art direction &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-media-queries-to-provide-high-res-images-or-art-direction&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Media queries can create rules based on the
&lt;a href=&quot;http://www.html5rocks.com/en/mobile/high-dpi/#toc-bg&quot; rel=&quot;noopener&quot;&gt;device pixel ratio&lt;/a&gt;,
making it possible to specify different images for 2x versus 1x displays.&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-resolution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2dppx&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 property&quot;&gt;-webkit-min-device-pixel-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&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 comment&quot;&gt;/* High dpi styles &amp;amp; resources here */&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;Chrome, Firefox, and Opera all support the standard &lt;code&gt;(min-resolution: 2dppx)&lt;/code&gt;,
while the Safari and Android browsers both require the older vendor prefixed
syntax without the &lt;code&gt;dppx&lt;/code&gt; unit.  Remember, these styles are only loaded if the
device matches the media query, and you must specify styles for the base case.
This also provides the benefit of ensuring something is rendered if the browser
doesn&#39;t support resolution-specific media queries.&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 selector&quot;&gt;.sample&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 128px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 128px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;icon1x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;@media &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-resolution&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2dppx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* Standard syntax */&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;-webkit-min-device-pixel-ratio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;/* Safari &amp;amp; Android Browser */&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;.sample&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;background-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; contain&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;icon2x.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/media-query-dppx.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can also use the min-width syntax to display alternative images depending on
the viewport size.  This technique has the advantage that the image is not
downloaded if the media query doesn&#39;t match.  For example, &lt;code&gt;bg.png&lt;/code&gt; is only
downloaded and applied to the &lt;code&gt;body&lt;/code&gt; if the browser width is 500px or greater:&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;@media&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 500px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;bg.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;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;h2 id=&quot;use-svg-for-icons&quot;&gt;Use SVG for icons &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-svg-for-icons&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When adding icons to your page, use SVG icons where possible or in some
cases, unicode characters.&lt;/p&gt;
&lt;h3 id=&quot;summary-3&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#summary-3&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Use SVG or unicode for icons instead of raster images.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;replace-simple-icons-with-unicode&quot;&gt;Replace simple icons with unicode &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#replace-simple-icons-with-unicode&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many fonts include support for the myriad of unicode glyphs, which can be used
instead of images. Unlike images, unicode fonts scale well and look good no
matter how small or large they appear on screen.&lt;/p&gt;
&lt;p&gt;Beyond the normal character set, unicode may include symbols for
arrows (←), math operators (√), geometric shapes
(★), control pictures (▶), music notation (♬),
Greek letters (Ω), even chess pieces (♞).&lt;/p&gt;
&lt;p&gt;Including a unicode character is done in the same way named entities are:
&lt;code&gt;&amp;amp;#XXXX&lt;/code&gt;, where &lt;code&gt;XXXX&lt;/code&gt; represents the unicode character number. For example:&lt;/p&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;You&#39;re a super &lt;span class=&quot;token entity&quot; title=&quot;&amp;#9733;&quot;&gt;&amp;amp;#9733;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You&#39;re a super ★&lt;/p&gt;
&lt;h3 id=&quot;replace-complex-icons-with-svg&quot;&gt;Replace complex icons with SVG &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#replace-complex-icons-with-svg&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For more complex icon requirements, SVG icons are generally lightweight,
easy to use, and can be styled with CSS. SVG have a number of advantages over
raster images:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They&#39;re vector graphics that can be infinitely scaled.&lt;/li&gt;
&lt;li&gt;CSS effects such as color, shadowing, transparency, and animations are
straightforward.&lt;/li&gt;
&lt;li&gt;SVG images can be inlined right in the document.&lt;/li&gt;
&lt;li&gt;They are semantic.&lt;/li&gt;
&lt;li&gt;They provide better accessibility with the appropriate attributes.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;With SVG icons, you can either add icons using inline SVG, like&lt;br /&gt;this checkmark:&lt;br /&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;svg&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;version&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;1.1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;xmlns&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;http://www.w3.org/2000/svg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;       &lt;span class=&quot;token attr-name&quot;&gt;&lt;span class=&quot;token namespace&quot;&gt;xmlns:&lt;/span&gt;xlink&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;http://www.w3.org/1999/xlink&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;width&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;32&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;32&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;viewBox&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;0 0 32 32&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;path&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;d&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;M27 4l-15 15-7-7-5 5 12 12 20-20z&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;fill&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;#000000&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;path&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;svg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;or by using an image tag, like this credit card icon:&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;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;credit.svg&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/icon-svg.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;use-icon-fonts-with-caution&quot;&gt;Use icon fonts with caution &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-icon-fonts-with-caution&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
  &lt;img alt=&quot;Example of a page that uses FontAwesome for its font icons.&quot; class=&quot;float-right&quot; decoding=&quot;async&quot; height=&quot;568&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 320px) 320px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oHSWVw7IvaO8QFdrKzmK.png?auto=format&amp;w=640 640w&quot; width=&quot;320&quot; /&gt;
  &lt;figcaption&gt;
    &lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/icon-font.html&quot; target=&quot;_blank&quot; class=&quot;external&quot;&gt;
      Example of a page that uses FontAwesome for its font icons.
    &lt;/a&gt;
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Icon fonts are popular, and can be easy to use, but have some drawbacks
compared to SVG icons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They&#39;re vector graphics that can be infinitely scaled, but may be
anti-aliased resulting in icons that aren’t as sharp as expected.&lt;/li&gt;
&lt;li&gt;Limited styling with CSS.&lt;/li&gt;
&lt;li&gt;Pixel perfect positioning can be difficult, depending on line-height,
letter spacing, etc.&lt;/li&gt;
&lt;li&gt;They aren&#39;t semantic, and can be difficult to use with screen readers or
other assistive technology.&lt;/li&gt;
&lt;li&gt;Unless properly scoped, they can result in a large file size for only using a
small subset of the icons available.&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;With Font Awesome, you can either add icons by using a unicode&lt;br /&gt;entity, like this HTML5 logo (&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;span&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;awesome&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 entity&quot; title=&quot;&amp;#xf13b;&quot;&gt;&amp;amp;#xf13b;&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;span&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;)&lt;br /&gt;or by adding special classes to an &lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;lt;&quot;&gt;&amp;amp;lt;&lt;/span&gt;i&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt; element like the CSS3&lt;br /&gt;logo (&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;i&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;fa fa-css3&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;i&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;&lt;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/icon-font.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There are hundreds of free and paid icon fonts available including &lt;a href=&quot;https://fortawesome.github.io/Font-Awesome/&quot; rel=&quot;noopener&quot;&gt;Font
Awesome&lt;/a&gt;,
&lt;a href=&quot;http://pictos.cc/&quot; rel=&quot;noopener&quot;&gt;Pictos&lt;/a&gt;, and &lt;a href=&quot;https://glyphicons.com/&quot; rel=&quot;noopener&quot;&gt;Glyphicons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Be sure to balance the weight of the additional HTTP request and file size with
the need for the icons. For example, if you only need a handful of icons, it
may be better to use an image or an image sprite.&lt;/p&gt;
&lt;h2 id=&quot;optimize-images-for-performance&quot;&gt;Optimize images for performance &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#optimize-images-for-performance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Images often account for most of the downloaded bytes and also often occupy
a significant amount of the visual space on the page. As a result, optimizing
images can often yield some of the largest byte savings and performance
improvements for your website: the fewer bytes the browser has to download,
the less competition there is for client&#39;s bandwidth and the faster the
browser can download and display all the assets.&lt;/p&gt;
&lt;h3 id=&quot;summary-4&quot;&gt;Summary &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#summary-4&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Don&#39;t just randomly choose an image format—understand the different
formats available and use the format best suited.&lt;/li&gt;
&lt;li&gt;Include image optimization and compression tools into your workflow to reduce
file sizes.&lt;/li&gt;
&lt;li&gt;Reduce the number of http requests by placing frequently used images into
image sprites.&lt;/li&gt;
&lt;li&gt;To improve the initial page load time and reduce the initial page weight,
consider loading images only after they’ve scrolled into view.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;choose-the-right-format&quot;&gt;Choose the right format &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#choose-the-right-format&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two types of images to consider:
&lt;a href=&quot;https://en.wikipedia.org/wiki/Vector_graphics&quot; rel=&quot;noopener&quot;&gt;vector images&lt;/a&gt;
and &lt;a href=&quot;https://en.wikipedia.org/wiki/Raster_graphics&quot; rel=&quot;noopener&quot;&gt;raster images&lt;/a&gt;.
For raster images, you also need to choose the right compression format,
for example: &lt;code&gt;GIF&lt;/code&gt;, &lt;code&gt;PNG&lt;/code&gt;, &lt;code&gt;JPG&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Raster images&lt;/strong&gt;, like photographs and other images, are represented as a grid
of individual dots or pixels. Raster images typically come from a camera or
scanner, or can be created in the browser with the &lt;code&gt;canvas&lt;/code&gt; element.  As the
image size gets larger, so does the file size.  When scaled larger than their
original size, raster images become blurry because the browser needs to guess
how to fill in the missing pixels.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vector images&lt;/strong&gt;, such as logos and line art, are defined by a set of curves,
lines, shapes, and fill colors. Vector images are created with programs like
Adobe Illustrator or Inkscape and saved to a vector format like
&lt;a href=&quot;https://css-tricks.com/using-svg/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;SVG&lt;/code&gt;&lt;/a&gt;.  Because vector images are built on
simple primitives, they can be scaled without any loss in quality or
change in file size.&lt;/p&gt;
&lt;p&gt;When choosing the appropriate format, it is important to consider both the
origin of the image (raster or vector), and the content (colors, animation,
text, etc). No one format fits all image types, and each has its own strengths
and weaknesses.&lt;/p&gt;
&lt;p&gt;Start with these guidelines when choosing the appropriate format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;JPG&lt;/code&gt; for photographic images.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;SVG&lt;/code&gt; for vector art and solid color graphics such as logos and line art.
If vector art is unavailable, try &lt;code&gt;WebP&lt;/code&gt; or &lt;code&gt;PNG&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;PNG&lt;/code&gt; rather than &lt;code&gt;GIF&lt;/code&gt; as it allows for more colors and offers better
compression ratios.&lt;/li&gt;
&lt;li&gt;For longer animations consider using &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt;, which provides better image
quality and gives the user control over playback.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;reduce-the-file-size&quot;&gt;Reduce the file size &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#reduce-the-file-size&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;You can reduce image file size considerably by &amp;quot;post-processing&amp;quot; the images
after saving. There are a number of tools for image compression—lossy and
lossless, online, GUI, command line.  Where possible, it&#39;s best to try
automating image optimization so that it&#39;s a built-in to your
workflow.&lt;/p&gt;
&lt;p&gt;Several tools are available that perform further, lossless compression on &lt;code&gt;JPG&lt;/code&gt;
and &lt;code&gt;PNG&lt;/code&gt; files with no effect on image quality. For &lt;code&gt;JPG&lt;/code&gt;, try
&lt;a href=&quot;http://jpegclub.org/&quot; rel=&quot;noopener&quot;&gt;jpegtran&lt;/a&gt; or
&lt;a href=&quot;http://freshmeat.net/projects/jpegoptim/&quot; rel=&quot;noopener&quot;&gt;jpegoptim&lt;/a&gt; (available on Linux only;
run with the --strip-all option). For &lt;code&gt;PNG&lt;/code&gt;, try
&lt;a href=&quot;http://optipng.sourceforge.net/&quot; rel=&quot;noopener&quot;&gt;OptiPNG&lt;/a&gt; or
&lt;a href=&quot;http://www.advsys.net/ken/util/pngout.htm&quot; rel=&quot;noopener&quot;&gt;PNGOUT&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;use-image-sprites&quot;&gt;Use image sprites &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-image-sprites&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;figure&gt;
&lt;img alt=&quot;Image sprite sheet used in example&quot; class=&quot;float-right&quot; decoding=&quot;async&quot; height=&quot;352&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 190px) 190px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=190 190w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=217 217w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=247 247w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=281 281w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=321 321w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=366 366w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/lIYXu84fRjlUCkSRomTQ.png?auto=format&amp;w=380 380w&quot; width=&quot;190&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;CSS spriting is a technique whereby a number of images are combined into a single
&amp;quot;sprite sheet&amp;quot; image. You can then use individual images by specifying the
background image for an element (the sprite sheet) plus an offset to display the
correct part.&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 selector&quot;&gt;.sprite-sheet&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;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&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;sprite-sheet.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 25px&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 selector&quot;&gt;.google-logo&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 125px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 45px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -190px -170px&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 selector&quot;&gt;.gmail&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;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -150px -210px&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 selector&quot;&gt;.maps&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;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 40px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;  &lt;span class=&quot;token property&quot;&gt;background-position&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -120px -165px&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;a href=&quot;https://googlesamples.github.io/web-fundamentals/fundamentals/design-and-ux/responsive/image-sprite.html&quot; rel=&quot;noopener&quot;&gt;Try it&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Spriting has the advantage of reducing the number of downloads required to get
multiple images, while still enabling caching.&lt;/p&gt;
&lt;h3 id=&quot;consider-lazy-loading&quot;&gt;Consider lazy loading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#consider-lazy-loading&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Lazy loading can significantly speed up loading on long pages that include many
images below the fold by loading them either as needed or when the primary
content has finished loading and rendering.  In addition to performance
improvements, using lazy loading can create infinite scrolling experiences.&lt;/p&gt;
&lt;p&gt;Be careful when creating infinite scrolling pages—because content is loaded as
it becomes visible, search engines may never see that content.  In addition,
users who are looking for information they expect to see in the footer,
never see the footer because new content is always loaded.&lt;/p&gt;
&lt;h2 id=&quot;avoid-images-completely&quot;&gt;Avoid images completely &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#avoid-images-completely&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes the best image isn&#39;t actually an image at all. Whenever possible, use
the native capabilities of the browser to provide the same or similar
functionality.  Browsers generate visuals that would have previously required
images.   This means that browsers no longer need to download separate image
files thus preventing awkwardly scaled images.  You can use unicode or special
icon fonts to render icons.&lt;/p&gt;
&lt;h3 id=&quot;place-text-in-markup-instead-of-embedded-in-images&quot;&gt;Place text in markup instead of embedded in images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#place-text-in-markup-instead-of-embedded-in-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Wherever possible, text should be text and not embedded into images. For
example, using images for headlines or placing contact information—like
phone numbers or addresses—directly into images prevents users from
copying and pasting the information; it makes the information inaccessible for
screen readers, and it isn&#39;t responsive.  Instead, place the text in your markup
and if necessary use webfonts to achieve the style you need.&lt;/p&gt;
&lt;h3 id=&quot;use-css-to-replace-images&quot;&gt;Use CSS to replace images &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/responsive-images/#use-css-to-replace-images&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Modern browsers can use CSS features to create styles that would previously have
required images.  For example: complex gradients can be created using the
&lt;code&gt;background&lt;/code&gt; property, shadows can be created using &lt;code&gt;box-shadow&lt;/code&gt;, and rounded
corners can be added with the &lt;code&gt;border-radius&lt;/code&gt; property.&lt;/p&gt;
&lt;style&gt;
  p#noImage {
    margin-top: 2em;
    padding: 1em;
    padding-bottom: 2em;
    color: white;
    border-radius: 5px;
    box-shadow: 5px 5px 4px 0 rgba(9,130,154,0.2);
    background: linear-gradient(rgba(9, 130, 154, 1), rgba(9, 130, 154, 0.5));
  }

  p#noImage code {
    color: rgb(64, 64, 64);
  }
&lt;/style&gt;
&lt;p id=&quot;noImage&quot;&gt;
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque sit
amet augue eu magna scelerisque porta ut ut dolor. Nullam placerat egestas
nisl sed sollicitudin. Fusce placerat, ipsum ac vestibulum porta, purus
dolor mollis nunc, pharetra vehicula nulla nunc quis elit. Duis ornare
fringilla dui non vehicula. In hac habitasse platea dictumst. Donec
ipsum lectus, hendrerit malesuada sapien eget, venenatis tempus purus.
&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 selector&quot;&gt;&amp;lt;style&gt;&lt;br /&gt;    div#noImage&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;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; white&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token property&quot;&gt;box-shadow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 5px 5px 4px 0 &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;130&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;154&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;0.2&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 property&quot;&gt;background&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;linear-gradient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 130&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 154&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rgba&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;9&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 130&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 154&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0.5&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span 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;&amp;lt;/style&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Keep in mind that using these techniques does require rendering cycles, which
can be significant on mobile.  If over-used, you&#39;ll lose any benefit you may
have gained and it may hinder performance.&lt;/p&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Movi Kanti Revo - Part 1 - Building the 3D World</title>
    <link href="https://web.dev/movikantirevo/"/>
    <updated>2012-09-19T00:00:00Z</updated>
    <id>https://web.dev/movikantirevo/</id>
    <content type="html" mode="escaped">&lt;figure&gt;
&lt;img alt=&quot;Movi Kanti Revo logo.&quot; decoding=&quot;async&quot; height=&quot;280&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/btqFngSls9zPrOREEqAU.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;/figure&gt;
&lt;p&gt;&lt;a href=&quot;http://www.movikantirevo.com/&quot; rel=&quot;noopener&quot;&gt;Movi.Kanti.Revo&lt;/a&gt; is
a new sensory Chrome experiment crafted by Cirque du Soleil and developed by
Subatomic Systems that brings the wonder of Cirque du Soleil to the web through
modern web technologies.&lt;/p&gt;
&lt;h2 id=&quot;building-the-3d-world&quot;&gt;Building the 3D World &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#building-the-3d-world&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The experiment was created using just HTML5, and the environment is built
entirely with markup and CSS.  Like set pieces on
stage, &lt;code&gt;div&lt;/code&gt;s, &lt;code&gt;img&lt;/code&gt;s, small &lt;code&gt;video&lt;/code&gt;s and
other elements are positioned in a 3D space using CSS.  Using the new
&lt;code&gt;getUserMedia&lt;/code&gt; API enabled a whole new way of interacting with the
experiment, instead of using the keyboard or mouse, a JavaScript facial
detection library tracks your head and moves the environment along with you.&lt;/p&gt;
&lt;h2 id=&quot;all-the-webs-a-stage&quot;&gt;All the Web&#39;s a Stage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#all-the-webs-a-stage&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To build this experiment, it’s best to imagine the browser as a stage, and the
elements like &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s, images, videos and other elements as
set pieces positioned in 3D space using CSS.  Each element, or set piece is
positioned on the stage by applying a 3D transform.  If you’re unfamiliar with
the &lt;code&gt;translate3d&lt;/code&gt; transform, it takes 3 parameters, X, Y and Z.  X
moves the element along a horizontal line, Y moves the element up and down,
and Z moves the element closer or further away.  For example, applying a
&lt;code&gt;transform: translate3d(100px, -200px, 300px)&lt;/code&gt; will move the element
100 pixels to the right, 200 pixels down and 300 pixels closer towards the
viewer.&lt;/p&gt;
&lt;h3 id=&quot;building-the-auditorium&quot;&gt;Building the Auditorium &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#building-the-auditorium&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s take a look at the &lt;a href=&quot;http://www.movikantirevo.com/#sea,&quot; rel=&quot;noopener&quot;&gt;last
scene&lt;/a&gt; and to see how it’s put together.  All scenes are broken down into
three primary containers, the world container, a perspective container and
the stage.  The world container effectively setups up the viewers camera, and
uses the CSS &lt;code&gt;perspective&lt;/code&gt; property to tell the browser where the
viewer will be looking at the element from.  The
&lt;code&gt;#perspective-container&lt;/code&gt; is used to change our perspective by
applying 3D transforms to it.  Finally, the stage contains the actual set
pieces that will be visible on screen.&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 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;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;world-container&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;div&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;perspective-container&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;div&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;stage&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;div&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;div&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;div&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;div&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;#world-container&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;perspective&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 700px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; hidden&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 selector&quot;&gt;#perspective-container&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; % mixin &lt;span class=&quot;token property&quot;&gt;transform-style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; preserve-3d&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; % mixin &lt;span class=&quot;token property&quot;&gt;transform-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center center&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; % mixin &lt;span class=&quot;token property&quot;&gt;perspective-origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center center&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; % mixin &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate3d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;rotateZ&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0deg&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;h3 id=&quot;visualizing-the-stage&quot;&gt;Visualizing the Stage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#visualizing-the-stage&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Within the stage, there are seven elements in the final scene.  Moving from
back to front, they include the sky background, a fog layer, the doors, the
water, reflections, an additional fog layer, and finally the cliffs in front.
Each item is placed on stage with a
&lt;code&gt;transform: translate3d(x, y, z)&lt;/code&gt; CSS property that indicates where
it fits in 3D space. We used the z value in a similar way that we’d use
&lt;code&gt;z-index&lt;/code&gt;, but with the &lt;code&gt;translate3d&lt;/code&gt; property, we can
also provide a unit with the value.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Figure 1: The stage from the side&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/f8idh5Ffxbxd9muznYQN.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;figcaption&gt;
    Figure 1: The stage from the side.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Figure 1 shows the scene zoomed out and rotated almost 90 degrees so that you
can visualize the way each of the different set pieces are placed within the
stage.  At the back (furthest to the left), you can see the background, fog,
doors, water and finally the cliffs.&lt;/p&gt;
&lt;h3 id=&quot;placing-the-background-on-stage&quot;&gt;Placing the Background on Stage &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#placing-the-background-on-stage&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Let’s start with the background image.  Since it’s the furthest back, we
applied a -990px transform on the Z-axis to push it back in our perspective
(see Figure 2).&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;The stage, with only the background placed at -990px&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/G8jQnIyffHXwHAGQz26s.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;figcaption&gt;
    Figure 2: The stage, with only the background placed at -990px
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As it moves back in space, physics demands that it gets smaller, so it needs
to be resized via a &lt;code&gt;scale(3.3)&lt;/code&gt; property to fit the viewport and
aligned the top edge with the top of the viewport with a
&lt;code&gt;translate3d&lt;/code&gt; on the y-axis (see Figure 3).&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 selector&quot;&gt;.background&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;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1280px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 800px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;top&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;stars.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token selector&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; % mixin &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate3d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 786px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &amp;lt;b&gt;-990px&amp;lt;/b&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &amp;lt;b&gt;&lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;3.3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&amp;lt;/b&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;figure&gt;
&lt;img alt=&quot;Figure 3: The stage, with background positioned and scaled.&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/oIOqxVGPQtUillIvLyKx.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;figcaption&gt;
    Figure 3: The stage, with only the background positioned and scaled.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The fog, doors, and the cliffs in the same way, each by applying a
&lt;code&gt;translate3d&lt;/code&gt; with an appropriate z position and scale factor
(see Figure 4).  Notice how the fog is stacked behind the doors, and behind
the cliffs.&lt;/p&gt;
&lt;figure&gt;
&lt;img alt=&quot;Figure 4: The stage, with fog, doors and cliff positioned and scaled&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WR6Imi7KOP6ZqUhLlDHQ.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;figcaption&gt;
    Figure 4: The stage, with fog, doors and cliff positioned and scaled.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h3 id=&quot;adding-the-sea&quot;&gt;Adding the Sea &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#adding-the-sea&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To create as realistic an environment as possible, we knew we couldn’t simply
put the water on a vertical plane, it’s typically doesn’t exist that way in
the real world.  In addition to applying the &lt;code&gt;translate3d&lt;/code&gt; to
position the water on stage, we also apply an x-axis (horizontal) rotation of
60 degrees (&lt;code&gt;rotateX(60deg)&lt;/code&gt;) to make it appear flat and textured.
A similar rotation was added to the door reflection and secondary fog to make
it appear in the correct plane (see Figure 5).&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 selector&quot;&gt;.sea&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;bottom&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 120px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;background-image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &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;sea2.png&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1280px&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 283px&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; % mixin &lt;span class=&quot;token property&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;translate3d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;-100px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 225px&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; -30px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scale&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;2.3&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &amp;lt;b&gt;&lt;span class=&quot;token function&quot;&gt;rotateX&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;60deg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&amp;lt;/b&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;figure&gt;
&lt;img alt=&quot;Figure 5: The stage, with everything positioned and scaled.&quot; decoding=&quot;async&quot; height=&quot;400&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 640px) 640px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/T4FyVKpzu4WKF1kBNvXepbi08t52/WgTCZnJfO93yN1Tu0a3z.png?auto=format&amp;w=1280 1280w&quot; width=&quot;640&quot; /&gt;
&lt;figcaption&gt;
    Figure 5: The stage, with everything positioned and scaled.
&lt;/figcaption&gt;&gt;
&lt;/figure&gt;
&lt;p&gt;Each scene was built in a similar fashion, elements were visualized within the
3D space of a stage, and an appropriate transform was applied to each.&lt;/p&gt;
&lt;h2 id=&quot;further-reading&quot;&gt;Further Reading &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/movikantirevo/#further-reading&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://www.html5rocks.com/en/tutorials/3d/css/&quot; rel=&quot;noopener&quot;&gt;3D and CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://coding.smashingmagazine.com/2012/01/06/adventures-in-the-third-dimension-css-3-d-transforms/&quot; rel=&quot;noopener&quot;&gt;Adventures in the Third Dimension: CSS 3D Transforms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://desandro.github.com/3dtransforms/&quot; rel=&quot;noopener&quot;&gt;Intro to CSS 3D Transforms&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/docs/CSS/Using_CSS_transforms&quot; rel=&quot;noopener&quot;&gt;MDN: Using CSS Transforms&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
    <author>
      <name>Pete LePage</name>
    </author>
  </entry>
  
  <entry>
    <title>Read files in JavaScript</title>
    <link href="https://web.dev/read-files/"/>
    <updated>2010-06-18T00:00:00Z</updated>
    <id>https://web.dev/read-files/</id>
    <content type="html" mode="escaped">&lt;p&gt;Selecting and interacting with files on the user&#39;s local device is
one of the most commonly used features of the web. It allows users to select
files and upload them to a server, for example, uploading photos, or
submitting tax documents, etc. But, it also allows sites to read and
manipulate them without ever having to transfer the data across the network.&lt;/p&gt;
&lt;h2 id=&quot;the-modern-file-system-access-api&quot;&gt;The modern File System Access API &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#the-modern-file-system-access-api&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The File System Access API provides an easy way to both read from
and write to files and directories on the user&#39;s local system. It&#39;s currently
available in most Chromium-based browsers such as Chrome or Edge. To learn
more about it, see the &lt;a href=&quot;https://web.dev/file-system-access/&quot;&gt;File System Access API&lt;/a&gt; article.&lt;/p&gt;
&lt;p&gt;Since the File System Access API is not compatible with all browsers yet,
check out &lt;a href=&quot;https://github.com/GoogleChromeLabs/browser-fs-access&quot; rel=&quot;noopener&quot;&gt;browser-fs-access&lt;/a&gt;,
a helper library that uses the new API wherever it is available, but falls
back to legacy approaches when it is not.&lt;/p&gt;
&lt;h2 id=&quot;working-with-files,-the-classic-way&quot;&gt;Working with files, the classic way &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#working-with-files,-the-classic-way&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This guide shows you how to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Select files
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/read-files/#select-input&quot;&gt;Using the HTML input element&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/read-files/#select-dnd&quot;&gt;Using a drag-and-drop zone&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/read-files/#read-metadata&quot;&gt;Read file metadata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/read-files/#read-content&quot;&gt;Read a file&#39;s content&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;select&quot;&gt;Select files &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#select&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;select-input&quot;&gt;HTML input element &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#select-input&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The easiest way for users to select files is using the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/input/file&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt;&lt;/a&gt; element, which is supported in every
major browser. When clicked, it lets a user select a file, or multiple files
if the &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/input/file#Additional_attributes&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;multiple&lt;/code&gt;&lt;/a&gt; attribute is included, using
their operating system&#39;s built-in file selection UI. When the user finishes
selecting a file or files, the element&#39;s &lt;code&gt;change&lt;/code&gt; event fires. You can
access the list of files from &lt;code&gt;event.target.files&lt;/code&gt;, which is a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileList&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;FileList&lt;/code&gt;&lt;/a&gt; object. Each item in the &lt;code&gt;FileList&lt;/code&gt; is a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; object.&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;!-- The `multiple` attribute lets users select multiple files. --&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;type&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;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;file-selector&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;multiple&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;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;&lt;br /&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileSelector &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;file-selector&#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;  fileSelector&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;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;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 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; fileList &lt;span class=&quot;token operator&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;files&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileList&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;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;aside class=&quot;aside flow bg-state-info-bg color-state-info-text&quot;&gt;&lt;div class=&quot; flow&quot;&gt; Check if the &lt;a href=&quot;https://web.dev/file-system-access/#ask-the-user-to-pick-a-file-to-read&quot;&gt;&lt;code&gt;window.showOpenFilePicker()&lt;/code&gt;&lt;/a&gt; method is a viable alternative for your use case, since it also gives you a file handle so you can possibly write back to the file, in addition to reading. This method can be &lt;a href=&quot;https://github.com/GoogleChromeLabs/browser-fs-access#opening-files&quot;&gt;polyfilled&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;This example lets a user select multiple files using their operating system&#39;s
built-in file selection UI and then logs each selected file to the console.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/input-type-file?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;input-type-file on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h4 id=&quot;accept&quot;&gt;Limit the types of files users can select &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#accept&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;In some cases, you may want to limit the types of files users can select.
For example, an image editing app should only accept images, not text files.
To do that, add an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/HTML/Element/input/file#Additional_attributes&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;accept&lt;/code&gt;&lt;/a&gt; attribute to
the input element to specify which file types are accepted.&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 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;type&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;file&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&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;file-selector&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;accept&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;.jpg, .jpeg, .png&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h3 id=&quot;select-dnd&quot;&gt;Custom drag-and-drop &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#select-dnd&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In some browsers, the &lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt; element is also a drop target,
allowing users to drag-and-drop files into your app. But, the drop target is
small, and can be hard to use. Instead, once you&#39;ve provided the core
functionality using an &lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt; element, you can provide a
large, custom drag-and-drop surface.&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; Check if the &lt;a href=&quot;https://web.dev/file-system-access/#drag-and-drop-integration&quot;&gt;&lt;code&gt;DataTransferItem.getAsFileSystemHandle()&lt;/code&gt;&lt;/a&gt; method is a viable alternative for your use case, since it also gives you a file handle so you can possibly write back to the file, in addition to reading. &lt;/div&gt;&lt;/aside&gt;
&lt;h4 id=&quot;choose-drop-zone&quot;&gt;Choose your drop zone &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#choose-drop-zone&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Your drop surface depends on the design of your application. You may
only want part of the window to be a drop surface, or potentially the entire
window.&lt;/p&gt;
&lt;figure&gt;
  &lt;img alt=&quot;A screenshot of Squoosh, an image compression web app.&quot; decoding=&quot;async&quot; height=&quot;589&quot; loading=&quot;lazy&quot; sizes=&quot;(min-width: 800px) 800px, calc(100vw - 48px)&quot; src=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&quot; srcset=&quot;https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=200 200w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=228 228w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=260 260w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=296 296w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=338 338w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=385 385w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=439 439w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=500 500w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=571 571w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=650 650w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=741 741w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=845 845w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=964 964w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=1098 1098w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=1252 1252w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=1428 1428w, https://web-dev.imgix.net/image/tcFciHGuF3MxnTr1y5ue01OGLBn2/xX8UXdqkLmZXu3Ad1Z2q.png?auto=format&amp;w=1600 1600w&quot; width=&quot;800&quot; /&gt;
  &lt;figcaption&gt;
    Squoosh makes the entire window a drop zone.
  &lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Squoosh allows the user to drag and drop an image anywhere into the window,
and clicking &lt;strong&gt;select an image&lt;/strong&gt; invokes the &lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt; element.
Whatever you choose as your drop zone, make sure it&#39;s clear to the user that
they can drag and drop files onto that surface.&lt;/p&gt;
&lt;h4 id=&quot;define-drop-zone&quot;&gt;Define the drop zone &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#define-drop-zone&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;To enable an element to be a drag-and-drop zone, you&#39;ll need to listen for
two events, &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/dragover_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;dragover&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Document/drop_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;drop&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;dragover&lt;/code&gt;
event updates the browser UI to visually indicate that the drag-and-drop
action is creating a copy of the file. The &lt;code&gt;drop&lt;/code&gt; event is fired after the
user drops the files onto the surface. Similar to the input element,
you can access the list of files from &lt;code&gt;event.dataTransfer.files&lt;/code&gt;, which is
a &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileList&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;FileList&lt;/code&gt;&lt;/a&gt; object. Each item in the &lt;code&gt;FileList&lt;/code&gt; is a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; object.&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; dropArea &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;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;drop-area&#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;dropArea&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;dragover&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stopPropagation&lt;/span&gt;&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;  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 comment&quot;&gt;// Style the drag-and-drop as a &quot;copy file&quot; operation.&lt;/span&gt;&lt;br /&gt;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dropEffect &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;copy&#39;&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;br /&gt;&lt;br /&gt;dropArea&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;drop&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;  event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stopPropagation&lt;/span&gt;&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;  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;const&lt;/span&gt; fileList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dataTransfer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;files&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileList&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;&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Event/stopPropagation&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;event.stopPropagation()&lt;/code&gt;&lt;/a&gt; and
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/Event/preventDefault&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;event.preventDefault()&lt;/code&gt;&lt;/a&gt; stop the browser&#39;s default
behavior and allow your code to run instead. Without them,
the browser would otherwise navigate away from your page and open the files
the user dropped into the browser window.&lt;/p&gt;
&lt;p&gt;Check out &lt;a href=&quot;https://custom-drag-and-drop.glitch.me/&quot; rel=&quot;noopener&quot;&gt;Custom drag-and-drop&lt;/a&gt; for a live demonstration.&lt;/p&gt;
&lt;h3 id=&quot;directories&quot;&gt;What about directories? &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#directories&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, today there isn&#39;t a good way to access a directory.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/HTMLInputElement/webkitdirectory&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;webkitdirectory&lt;/code&gt;&lt;/a&gt; attribute on the &lt;code&gt;&amp;lt;input type=&amp;quot;file&amp;quot;&amp;gt;&lt;/code&gt; element allows the user to choose a directory or directories. It&#39;s
&lt;a href=&quot;https://caniuse.com/#search=webkitdirectory&quot; rel=&quot;noopener&quot;&gt;supported in most major browsers&lt;/a&gt; except for Firefox
for Android.&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; Check if the &lt;a href=&quot;https://web.dev/file-system-access/#opening-a-directory-and-enumerating-its-contents&quot;&gt;&lt;code&gt;window.showDirectoryPicker()&lt;/code&gt;&lt;/a&gt; method is a viable alternative for your use case, since it also gives you a directory handle so you can possibly write back to the directory, in addition to reading. This method can be &lt;a href=&quot;https://github.com/GoogleChromeLabs/browser-fs-access#opening-directories&quot;&gt;polyfilled&lt;/a&gt;. &lt;/div&gt;&lt;/aside&gt;
&lt;p&gt;If drag-and-drop is enabled, a user may try to drag a directory into the
drop zone. When the drop event is fired, it will include a &lt;code&gt;File&lt;/code&gt; object for
the directory, but does not provide access any of the files within the
directory.&lt;/p&gt;
&lt;h2 id=&quot;read-metadata&quot;&gt;Read file metadata &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#read-metadata&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/File&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;File&lt;/code&gt;&lt;/a&gt; object contains metadata about
the file. Most browsers provide the file name, the size of the file, and the
MIME type, though depending on the platform, different browsers may provide
different, or additional information.&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMetadataForFileList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fileList&lt;/span&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;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; fileList&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;// Not supported in Safari for iOS.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;NOT SUPPORTED&#39;&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;// Not supported in Firefox for Android or Opera for Android.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;NOT SUPPORTED&#39;&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;// Unknown cross-browser support.&lt;/span&gt;&lt;br /&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; size &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;NOT SUPPORTED&#39;&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;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; size&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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;You can see this in action in the &lt;a href=&quot;https://input-type-file.glitch.me/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;input-type-file&lt;/code&gt;&lt;/a&gt; demo.&lt;/p&gt;
&lt;h2 id=&quot;read-content&quot;&gt;Read a file&#39;s content &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#read-content&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To read a file, use &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileReader&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;FileReader&lt;/code&gt;&lt;/a&gt;, which enables you to read
the content of a &lt;code&gt;File&lt;/code&gt; object into memory. You can instruct &lt;code&gt;FileReader&lt;/code&gt;
to read a file as an &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileReader/readAsArrayBuffer&quot; rel=&quot;noopener&quot;&gt;array buffer&lt;/a&gt;, a
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileReader/readAsDataURL&quot; rel=&quot;noopener&quot;&gt;data URL&lt;/a&gt;, or &lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileReader/readAsText&quot; rel=&quot;noopener&quot;&gt;text&lt;/a&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;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readImage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&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;// Check if the file is an image.&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;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startsWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;image/&#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;    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;File is not an image.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;type&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&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;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileReader&lt;/span&gt;&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;  reader&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;load&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;    img&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;src &lt;span class=&quot;token operator&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;result&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;br /&gt;  reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAsDataURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&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 example above reads a &lt;code&gt;File&lt;/code&gt; provided by the user, then converts it to a
data URL, and uses that data URL to display the image in an &lt;code&gt;img&lt;/code&gt; element.
Check out the &lt;a href=&quot;https://read-image-file.glitch.me/&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;read-image-file&lt;/code&gt;&lt;/a&gt; demo to see how to
verify that the user has selected an image file.&lt;/p&gt;
&lt;div class=&quot;glitch-embed-wrap&quot; style=&quot;height: 480px; width: 100%;&quot;&gt;
  &lt;iframe allow=&quot;camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi&quot; loading=&quot;lazy&quot; src=&quot;https://glitch.com/embed/#!/embed/read-image-file?attributionHidden=true&amp;sidebarCollapsed=true&amp;previewSize=100&quot; style=&quot;height: 100%; width: 100%; border: 0;&quot; title=&quot;read-image-file on Glitch&quot;&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 id=&quot;monitor-progress&quot;&gt;Monitor the progress of a file read &lt;a class=&quot;headline-link&quot; href=&quot;https://web.dev/read-files/#monitor-progress&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When reading large files, it may be helpful to provide some UX to indicate
how far the read has progressed. For that, use the
&lt;a href=&quot;https://developer.mozilla.org/docs/Web/API/FileReader/progress_event&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;progress&lt;/code&gt;&lt;/a&gt; event provided by &lt;code&gt;FileReader&lt;/code&gt;. The
&lt;code&gt;progress&lt;/code&gt; event provides two properties, &lt;code&gt;loaded&lt;/code&gt; (the amount read) and
&lt;code&gt;total&lt;/code&gt; (the amount 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;highlight-line&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;readFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; reader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FileReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  reader&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;load&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; result &lt;span class=&quot;token operator&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;result&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;    &lt;span class=&quot;token comment&quot;&gt;// Do something with result&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;/span&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  reader&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;progress&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;event&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;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;loaded &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;total&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; percent &lt;span class=&quot;token operator&quot;&gt;=&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;loaded &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;total&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;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Progress: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;percent&lt;span class=&quot;token punctuation&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;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/mark&gt;&lt;br /&gt;&lt;mark class=&quot;highlight-line highlight-line-active&quot;&gt;  &lt;span class=&quot;token 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;/mark&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;  reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readAsDataURL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class=&quot;highlight-line&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Hero image by Vincent Botta from &lt;a href=&quot;https://unsplash.com/photos/bv_rJXpNU9I&quot; rel=&quot;noopener&quot;&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
</content>
    <author>
      <name>Kayce Basques</name>
    </author><author>
      <name>Pete LePage</name>
    </author><author>
      <name>Thomas Steiner</name>
    </author>
  </entry>
</feed>
