@samthor sam thorogood · about sam developer relations on "web" varied projects:...
TRANSCRIPT
PWA-ifying Santa Tracker
Sam Thorogood
@samthor
About Sam
● Developer Relations on "Web"
● Varied projects: polyfills, apps, demos, videos, talks,internal + public advocacy
Demo
Why?
Open-Source
Education focus
Talks
Ghosts of Holidays Past
Half screen photo slide if text is necessary
2014
● Polymer 0.5
● With two polyfills-○ Shadow DOM○ Object.observe
● Horizontal design wasn't intuitive
Half screen photo slide if text is necessary
2015
● Total responsive village redesign
● Removed polyfills in favour of "production-ready Polymer 1.2"
● … lots of bugs, especially around element upgrading and O.o
2016
● Polymer fixed most weird bugs○ Binding to upgraded elements○ Used app-layout elements
● New landing pages and routing code, improved shareability
● Better use of shared elements for scoreboards, sharing, ...
2017
● Back to Shadow DOM polyfill with Polymer 2.0+○ Supported on Chrome/Safari
● Some surprises?
Fast Reliable Engagingusers have low attention..
squirrel
Fast Reliable Engagingoffline + app-like caching
Fast Reliable Engagingapp-like: fullscreen,
notifications, modern APIs
Santa as a Progressive Web App
Add To Home Screen
● You can already "ATHS"—this refers to the prompt○ Supported on Android
● Naïvely coerce it○ … just add a Web App Manifest
and a basic Service Worker
Add To Home Screen
10% of Santa Tracker's eligible sessions started here
<link rel="manifest" href="manifest.json" />
<script>
if ('serviceWorker' in navigator) {
window.sw = navigator.serviceWorker.register(
'/sw.js?lang=' + document.documentElement.lang);
}
</script>
Install Service Worker where available
Manifest describing home screen UX
<link rel="manifest" href="manifest.json" />
<script>
if ('serviceWorker' in navigator) {
window.sw = navigator.serviceWorker.register(
'/sw.js?lang=' + document.documentElement.lang);
}
</script>
<!-- non-standard meta tags -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#00c6ed">
<meta name="msapplication-TileColor" content="#30B443">
<meta name="msapplication-tap-highlight" content="no">
<!-- icons -->
<link rel="apple-touch-icon" sizes="76x76" href="/images/icon-76.png">
<link rel="apple-touch-icon" sizes="120x120" href="/images/icon-120.png">
<link rel="apple-touch-icon" sizes="152x152" href="/images/icon-152.png">
<link rel="apple-touch-icon" sizes="167x167" href="/images/icon-167.png">
<link rel="apple-touch-icon" sizes="180x180" href="/images/icon-180.png">
<link id="favicon" rel="shortcut icon" href="/images/favicon-16.png" sizes="16x16">
<link id="favicon" rel="shortcut icon" href="/images/favicon-32.png" sizes="32x32">
<link id="favicon" rel="shortcut icon" href="/images/favicon-64.png" sizes="64x64">
<link id="favicon" rel="shortcut icon" href="/images/favicon-96.png" sizes="96x96">
But… still need this cruft
var manifest = document.createElement('link');
manifest.rel = 'manifest';
manifest.crossOrigin = 'use-credentials';
manifest.href = 'manifest.json'; // avoid href rewriting from gulpfile
document.head.appendChild(manifest);
if ('serviceWorker' in navigator) {
window.sw = navigator.serviceWorker.register(
'/sw.js?lang=' + document.documentElement.lang);
// Reload the page when a new SW takes over. This should only happen in staging.
navigator.serviceWorker.addEventListener('controllerchange', function() {
location.reload();
});
}
We actually link manifest in JS
Offline
● We cache assets and work offline via aService Worker
● This is a script that:a) has a lifetime, including installb) proxies network requests
Challenges
● Santa is ~226mb ⚠�○ We can't/won't install all of it
● Languages: +Some different formats (mp3/ogg, gif/mp4)
● Continuous releases throughout December
Static vs prod
maps.gstatic.com/santatracker
● Unique URLs per-version—cached forever!
● Not just for offline loading
santatracker.google.com
Low cache expiry, to detect new release
Static
Prod
Load what you use
● Cache core of Santa on 'install'○ aka the "App Shell"
● Cache eligible scenes when user interacts with them○ The village itself is a scene
● Show offline via frozen houses○ ...but UX is hard
<lazy-pages selected-item="village">
<dorf-scene id="village" route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
slot="santa-scene" allow-page-scrolling></dorf-scene>
<boatload-scene route="boatload" icon="26f5"
path$="scenes/boatload/boatload-scene_[[language]].html"
loading-bg-color="#8fd7f7"
loading-src="scenes/boatload/img/loading.svg"
logo="scenes/boatload/img/logo.svg"
slot="santa-scene"></boatload-scene>
"village" route is loaded
<lazy-pages selected-item="village">
<dorf-scene id="village" route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
slot="santa-scene" allow-page-scrolling></dorf-scene>
<boatload-scene route="boatload" icon="26f5"
path$="scenes/boatload/boatload-scene_[[language]].html"
loading-bg-color="#8fd7f7"
loading-src="scenes/boatload/img/loading.svg"
logo="scenes/boatload/img/logo.svg"
slot="santa-scene"></boatload-scene>
"boatload-scene" is HTMLUnknownElement until ready
<lazy-pages selected-item="boatload">
<dorf-scene id="village" route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
slot="santa-scene" allow-page-scrolling></dorf-scene>
<boatload-scene route="boatload" icon="26f5"
path$="scenes/boatload/boatload-scene_[[language]].html"
loading-bg-color="#8fd7f7"
loading-src="scenes/boatload/img/loading.svg"
logo="scenes/boatload/img/logo.svg"
slot="santa-scene"></boatload-scene>
Causes boatload scene to be loaded
Request flow
v1/boatload-sc
ene_en.html
Response flow
v1/boatload-scene_en.html
['village', 'boatload', ...]
Install flow
Install flow
Install flow
Other APIs
Web Share
● New, unifying API for interacting with native share interface
● It replaces the sea of share buttons normally presented to users
● Available in Chrome 61+
Google Analytics
● About ¾ of Santa browsers support Web Components + Shadow DOM natively
● We log all JS errors with an onerror handler○ … although we messed up this year as JS errors from an
iframe have their stack trace stripped
Santa as a Polymer App
<dom-module id="santa-app">
<template>
<santa-strings id="strings"></santa-strings>
<iron-a11y-keys keys="esc enter" on-keys-pressed="_onKeysPressed"></...>
<preload-overlay id="preloader"></preload-overlay>
<santa-state during-tracker="{{duringTracker}}" ...></santa-state>
<santa-tracker-router route="{{route}}"></santa-tracker-router>
<santa-chrome id="chrome" pause="{{userPause}}" ...>
<lazy-pages selected-item="{{selectedScene}}" ...>
<dorf-scene route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
class="santa-scene" allow-page-scrolling></dorf-scene>
<!-- More scenes (30+) below -->
Lazy loads Polymer elements at runtime
Shows friendly loader
Preloader
● Hides:○ HTML import of scene itself○ Loading images, audio etc
● Matching image used on initial site load, to gain user attention
<dom-module id="santa-app">
<template>
<santa-strings id="strings"></santa-strings>
<iron-a11y-keys keys="esc enter" on-keys-pressed="_onKeysPressed"></...>
<preload-overlay id="preloader"></preload-overlay>
<santa-state during-tracker="{{duringTracker}}" ...></santa-state>
<santa-tracker-router route="{{route}}"></santa-tracker-router>
<santa-chrome id="chrome" pause="{{userPause}}" ...>
<lazy-pages selected-item="{{selectedScene}}" ...>
<dorf-scene route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
class="santa-scene" allow-page-scrolling></dorf-scene>
<!-- More scenes (30+) below -->
Takes /boatload.html and converts to route
route="codeboogie"
<dom-module id="santa-app">
<template>
<santa-strings id="strings"></santa-strings>
<iron-a11y-keys keys="esc enter" on-keys-pressed="_onKeysPressed"></...>
<preload-overlay id="preloader"></preload-overlay>
<santa-state during-tracker="{{duringTracker}}" ...></santa-state>
<santa-tracker-router route="{{route}}"></santa-tracker-router>
<santa-chrome id="chrome" pause="{{userPause}}" ...>
<lazy-pages selected-item="{{selectedScene}}" ...>
<dorf-scene route="village" icon="1f384" permanent
path$="scenes/dorf/dorf-scene_[[language]].html"
class="santa-scene" allow-page-scrolling></dorf-scene>
<!-- More scenes (30+) below -->
Generated, shared strings for i18n
Singleton for Santa's position, date within Dec
Weather
● The <santa-weather> element wraps a canvas using rAF to draw snow and clouds
Chrome/Badge ● The <santa-chrome> element
provides the top bar, nav, sidebar etc, wrapping app-layout elements
● We also have <santa-badge>, which exists on its own: it is owned by the app, and is slotted in the top center
Finally...
More Resources
● 2014: How 20% engineers built Santa Trackerhttps://youtu.be/dPb-QLuX_Xs
● Keep an eye on Chrome Developers on YouTube
● "pwacompat"
Sam Thorogood
@samthor
Ho Ho Ho