high fidelity

prototypes

surpassing the uncanny valley

Target UX

  1. Mobile or desktop app
  2. Launch from coveted system drawer
  3. Load instant & full screen
  4. Seamless demo flow

Why?

  1. User testing
  2. Get funding
  3. Sell a client

UX flow worth 1k words

Prototype worth 1k meetings

How

  1. A manifest
  2. Static assets
  3. The Web

optimize for first paint

Try It

I've fooled clients & bosses with prototypes like this

coinsafe.netlify.com

Rewind

Layer by layer let's enhance a prototype

1

Manifest

PWA entry point

Load It


              <link rel="manifest" href="/manifest.json">
            

App Name


              {
                "name": "YOUR_APP",
                "short_name": "YOUR_APP",
              }
            

Used on device homescreen

App Theme


              {
                "theme_color": "#ffffff",
                "background_color": "#ffffff",
              }
            

OS tints

App Icons


              {
                "icons": [
                  {
                    "src": "/icons/192x192.png",
                    "sizes": "192x192",
                    "type": "image/png",
                    "density": "4.0"
                  },
                  ...
                ]
              }
            

PWA Extras


              {
                "display": "standalone",
                "orientation": "portrait",
                "start_url": "/index.html",
                "scope": "/",
              }
            

Verify

Chrome Devtools can verify your work before you install on a device

2

<meta>

Inform the render engine

Viewport


              <meta name="viewport" content="initial-scale=1" />
            
  • Page width to device width
  • Remove 300ms tap delay

iOS Web App


              <meta name="mobile-web-app-capable" content="yes" />
              <meta name="application-name" content="YOUR_APP" />
            
  • Legacy?
  • Fullscreen
  • Links need love

App Theme


              <meta name="apple-mobile-web-app-status-bar-style" content="default" />
              <meta name="theme-color" content="#ffffff" />
              <meta name="msapplication-TileColor" content="#ffffff" />
            

App Brand


              <link rel="icon" sizes="192x192" href="/icons/192.png" />
              <link rel="apple-touch-icon" href="/icons/apple-icon.png" />
              <link rel="apple-touch-startup-image" href="/icons/icons/apple-splash.png" />
              <meta name="msapplication-TileImage" content="/icons/ms-icon.png" />
            

OS icons & launch assets

3

Icons

1st Impressions

Tips n' Tricks

  • Generator sites
  • Build scripts
  • Minimalism
  • Bespoke

Beware: icons can quickly become debt

4

Splash

2nd Impressions

iOS Tips n' Tricks

  • Generator sites
  • Build scripts

Tips n' Tricks

  • Manifest + icons
  • Not in full control
5

Prerender

SSR, compile, build, etc

Why?

  • Limit requests
  • Empower preload/prefetch/cache
  • Skip tombstones / skeletons
  • Spinnerless
  • On rails

Goal

Maintain an illusion,
not build for production

JAMstack to SPA (or JAMSPA 😏) are valid HTML experience creation options

6

Bundle

Like prerendered dependencies

Evaluate

Depending on the complexity of your prototype, you may have 0 to very little Javascript

Prioritize

  • Critical vs Non-critical
  • This before that

Chunk

Evaluation and prioritization are heavy informers on what bundle chunk opportunities exist

Differential Serving

  • JS
  • CSS
7

HTTPS

Native gateway

HTML5

  • Web share
  • Geolocation
  • Motion sensors
  • Camera access
  • and more...

PWA requirement

Not just a blocker to features, it's a requirement for promotion

Secure

You should want this

8

Preload

a declarative fetch directive

Preload

Cache ASAP, NO execution


              <link rel="preload" href="..." as="style">
              <link rel="preload" href="..." as="font" type="font/woff2" crossorigin>
              <link rel="preload" href="..." as="script">
              <link rel="preload" href="..." as="image">
            

NO delaying the page's onload

Good for:

  • Above the fold
  • Critical CSS
  • Critical fonts
  • Critical graphics
  • Critical scripts?

Prefetch

Lower priority items


              <link rel="prefetch" href="..." as="style">
              <link rel="prefetch" href="..." as="font" type="font/woff2" crossorigin>
              <link rel="prefetch" href="..." as="script">
              <link rel="prefetch" href="..." as="image">
            

Fetch while chillin

DNS Prefetch

Known off origin deps


              <link rel="dns-prefetch" href="...">
              <link rel="preconnect" href="...">
            

3rd party dependencies?

Media Query

conditional preload


              <link rel="preload" as="style" href="..." media="(orientation: portrait)">
              <link rel="preload" as="script" href="..." media="(min-width: 1200px)">
            

Prerender Precache

all page resources


              importScripts('.../workbox-sw.js');

              workbox.precaching.precacheAndRoute([
                'index.html',
                '/common.bundle.js',
                '/app.bundle.js',
                '/app.bundle.css',
                '/images/hero.png',
                ...
              ])
            
9

Defer

no render blocking here

Fetch These

asynchronously OR deferred


              <script defer src="..."></script>
              <script src="..." type="module"></script>
              <script async src="..." type="module"></script>
              
              <link rel="preload" as="style" href="..." 
                onload="this.rel='stylesheet'">
            

non-blocking resource loading

defer

Load async, then =>
execute in the order recieved


              <script defer src="..."></script>
              <script src="..." type="module"></script>
            

async

Load async, then =>
execute immediately


              <script async src="..."></script>
              <script async src="..." type="module"></script>
            

async CSS

Load async, then =>
apply immediately


              <link rel="preload" as="style" href="..." 
                onload="this.rel='stylesheet'">
            
10

service worker

Cache money

static files


              // service-worker.js

              workbox.routing.registerRoute(
                /\.(?:js|css)$/,
                new workbox.strategies.StaleWhileRevalidate(),
              );
            

media files


              // service-worker.js

              workbox.routing.registerRoute(
                /\.(?:png|gif|jpg|jpeg|svg)$/,
                new workbox.strategies.CacheFirst({
                  cacheName: 'images',
                }),
              ); 
            

dynamic files

🤷‍♂️

push notifications


              const localPush = async () => {
                const swRegistration = await navigator.serviceWorker.register('...')

                swRegistration.showNotification('...', {
                  body, icon, image, vibrate, // etc...
                });
              }
            

Can add a nice touch

11

Lazy Load

Optimize for first paint

Lazy Hooks

  1. Time
  2. Events
  3. Hints

Lazy Candidates

  • Uncriticals
  • Nice to have glyphs
  • Interaction required components
  • 3rd party

Lazy Tactics

  • Libs
  • Dynamic import
  • Attributes

              <img loading="lazy" src="...">
              <iframe loading="lazy" src="...">
            
12

Paint Holding

Chromium freeby

Without

With

nice to have

not critical, we've covered our bases

more benefits for !prototypes, but worth talking about

13

Fonts

tips n tricks

System Fonts


              body {
                font-family: system-ui, 
                  -apple-system, Segoe UI, 
                  Roboto, Ubuntu, Cantarell, 
                  Noto Sans, sans-serif, 
                  Segoe UI, Roboto, Ubuntu, 
                  Cantarell, Noto Sans, 
                  sans-serif;
              }
            

Boot faster & look native

Custom Fonts

Leverage preload and caching

Consider front loading

Variable Fonts

1 font to rule them all

Lazy Load

Non critical glyphs or whole families of a font can be loaded after the intial page load

14

CSS

Magic polish

Detection


              @media (display-mode: standalone) {
                ...
              }
            

Adjustments only if

Scrollviews

get that native scroll


              .scrollview {
                overflow-y: scroll;
                -webkit-overflow-scrolling: touch;
              }
            

Text Selection


              body {
                -webkit-user-select: none;
              }
            

Tap Highlight


              body {
                -webkit-tap-highlight-color: transparent;
              }
            
Source

Callouts


              body {
                -webkit-touch-callout: none;
              }
            
Source

Hover & Active


              <html>
                <head>...</head>
                <body ontouchstart>
                  ...
                </body>
              </html>
            

Sticky


              header {
                position: sticky;
                top: 0;
              }
            

Snap Points


              .filmstrip {
                overflow-x: scroll;
                overscroll-behavior-x: contain;
                scroll-snap-type: x mandatory;

                & > * {
                  scroll-snap-align: center;
                }
              }
            

<img />

Set sizes inline


              <img src="logo.png" height="50px" />
            

Jank Prevention: jumpless

15

Retina

Leverage that DPI

Prepare

optimize › crunch › process

host on the edge

<picture>


              <picture>
                <source type="image/webp" srcset="...@2x.webp 2x, ...@1x.webp 1x"/> 
                <source type="image/png" srcset="...@2x.png 2x, ...@1x.png 1x"/>
                <img height="200" src="..." alt="..." loading="lazy">
              </picture>
            

let the browser pick the best

Gotchas

  • Debt
  • Heavy
  • Performance
16

Pull 2 Refresh

Can ruin the illusion

Chromium


              .scrollview { 
                overscroll-behavior-y: contain;
              }
            

Keep the glow & stop navigational features

17

iOS Links

Stay in app...

A new window? Really?

really really.

Stay in app


              {
                "name": "Coinsafe",
                "short_name": "Coinsafe",
                "start_url": "/index.html"
              }
            

load the destination within this webview

18

Offline UX

show your work


              const { isOnline } = navigator
            

Listen


              window.addEventListener('online', e => 
                console.info('online'))

              window.addEventListener('offline', e => 
                console.info('offline'))
            

Lie-Fi

beware

UI

nice polish

that was..

18 steps

to a seamless PWototype

  • manifest.json
  • <meta>
  • icons
  • splash
  • bundle
  • HTTPS
  • defer
  • preload
  • precache
  • service workers
  • lazy load
  • paint holding
  • fonts
  • CSS
  • retina
  • pull to refresh
  • iOS links
  • offline UX

Healthy Apps

these implementation details are good for any project

Thank You

🤘💀🤘