Upgrade to Pro — share decks privately, control downloads, hide ads and more …

AMP tries to fix the web - What can we learn from it

stefan judis
November 12, 2016

AMP tries to fix the web - What can we learn from it

stefan judis

November 12, 2016
Tweet

More Decks by stefan judis

Other Decks in Technology

Transcript

  1. AMP tries to fix the web What can we learn

    from it? (◠‿◠✿) @stefanjudis
  2. Stefan Judis Frontend Developer, Occasional Teacher, Meetup Organizer ❤ Open

    Source, Performance and Accessibility ❤ @stefanjudis
  3. WHAT'S DISCUSSED IN THERE? Stop dummy history fast-forwarding Ignore clicks

    on iframes that have moved in the last 200ms Defer image loading until they are viewable Defer iframe loading until they are viewable Lower loading priority of cross-origin iframe
  4. “For many, reading on the mobile web is a slow,

    clunky and frustrating experience - but it doesn’t have to be that way.” Accelerated Mobile Pages Project
  5. <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>Hello

    World</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="amphtml" href="amp/hello-world.html"> <link rel="stylesheet" href="css/main.css"> </head> <body> <h1>Hello World!</h1> </body> </html> GETTING STARTED HTML file
  6. <!doctype html> <html> <head> <meta charset="utf-8"> <meta http-equiv="x-ua-compatible" content="ie=edge"> <title>Hello

    World</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="amphtml" href="amp/hello-world.html"> <link rel="stylesheet" href="css/main.css"> </head> <body> <h1>Hello World!</h1> </body> </html> GETTING STARTED HTML file
  7. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  8. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  9. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  10. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  11. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  12. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  13. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  14. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> GETTING STARTED AMP file
  15. GETTING STARTED AMP file HTML TAGS CAN BE USED UNCHANGED

    IN AMP HTML EXCEPT IMG, SCRIPT, VIDEO, IFRAME, ... https://www.ampproject.org/docs/reference/spec
  16. THREE BUILTINS AMP-IMG AMP-PIXEL AMP-VIDEO <amp-img src="/img/amp.jpg" width="1080" height="610" layout="responsive"

    alt="an image" ></amp-img> <amp-pixel src="https://foo.com/px?123" ></amp-pixel> <amp-video width="400" height="300" src="https://yourhost.com/myvideo.mp4" poster="poster.jpg"> <div fallback> <p>No support for HTML5</p> </div> <source type="video/mp4" src="foo.mp4"> <source type="video/webm" src="foo.webm"> </amp-video>
  17. CUSTOM MARKUP IF YOU NEED MORE ELEMENTS <script async custom-element="amp-sidebar"

    src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js"></script> <script async custom-element="amp-analytics" src="https://cdn.ampproject.org/v0/amp-analytics-0.1.js"></script>
  18. THREE BUILTINS AMP-IMG AMP-PIXEL AMP-VIDEO <amp-img src="/img/amp.jpg" width="1080" height="610" layout="responsive"

    alt="an image" ></amp-img> <amp-pixel src="https://foo.com/px?123" ></amp-pixel> <amp-video width="400" height="300" src="https://yourhost.com/myvideo.mp4" poster="poster.jpg"> <div fallback> <p>No support for HTML5</p> </div> <source type="video/mp4" src="foo.mp4"> <source type="video/webm" src="foo.webm"> </amp-video>
  19. CUSTOM MARKUP // src/custom-element.js export function registerElement(win, name, implementationClass) {

    win.document.registerElement(name, { prototype: createAmpElementProto(win, name), }); } https://github.com/ampproject/amphtml/blob/master/src/custom-element.js#L1429-L1435 // builtins/amp-img.js export function installImg(win) { registerElement(win, 'amp-img', AmpImg); } https://github.com/ampproject/amphtml/blob/master/builtins/amp-img.js#L145-L147
  20. CUSTOM MARKUP Custom Elements v0 Custom Elements v1 <hey-there></hey-there> var

    MyElement = document.registerElement( 'hey-there', { prototype: Object.create( HTMLElement.prototype, { createdCallback: { value: function() { console.log('registered'); }}, attachedCallback: { value: function() { console.log('live on DOM :-)'); }}, detachedCallback: { value: function() { console.log('leaving the DOM :-('); }}, attributeChangedCallback: {value: function( name, previousValue, value ) {}} }) } ); class HeyThere extends HTMLElement { constructor() { super(); console.log('registered'); } connectedCallback() { console.log('live on DOM :-)'); } disconnectedCallback() { console.log('leaving the DOM :-('); } attributeChangedCallback() {} } customElements.define('hey-there', HeyThere); http://codepen.io/stefanjudis/pen/ZpZzmY http://codepen.io/stefanjudis/pen/dpvyok
  21. CUSTOM MARKUP /** @override */ buildCallback() { /** @private @const

    {boolean} */ this.isPrerenderAllowed_ = !this.element.hasAttribute('noprerender'); } /** @override */ layoutCallback() { this.initialize_(); let promise = this.updateImageSrc_(); // We only allow to fallback on error on the initial layoutCallback // or else this would be pretty expensive. if (this.allowImgLoadFallback_) { promise = promise.catch(e => { this.onImgLoadingError_(); throw e; }); this.allowImgLoadFallback_ = false; } return promise; } https://github.com/ampproject/amphtml/blob/master/builtins/amp-img.js
  22. <img src="https://tracking.com/~text?tracking&txt=14&w=1&h=1"> <img src="https://tracking.com/~text?tracking&txt=15&w=1&h=1"> <!-- a lot more things here

    --> <img src="https://tracking.com/~text?tracking&txt=16&w=1&h=1"> <img src="https://tracking.com/~text?tracking&txt=17&w=1&h=1"> <!-- a lot more things here --> <img src="https://tracking.com/~text?tracking&txt=18&w=1&h=1"> <!-- a lot more things here --> <img height="335" width="900" src="http://some/path/image.jpg"> RESOURCE PRIORITY where the preloader fails
  23. RESOURCE PRIORITY the AMP runtime class AmpPixel extends BaseElement {

    getPriority() { // Loads after other content. return 1; } }
  24. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> REFLOW MANAGEMENT
  25. <!doctype html> <html ⚡> <head> <meta charset="utf-8"> <link rel="canonical" href="hello-world.html">

    <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1"> <style amp-boilerplate> body{ animation:-amp-start 8s steps(1,end) 0s 1 normal both } @keyframes -amp-start{ from{visibility:hidden} to{visibility:visible} } </style> <noscript> <style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms- animation:none;animation:none}</style> </noscript> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body>Hello World!</body> </html> REFLOW MANAGEMENT
  26. REFLOW MANAGEMENT EXTERNALLY FETCHED RESOURCES MUST BE GIVEN AN EXPLICIT

    SIZE amp-img { background-color: grey; } <amp-img src="https://chameleon-catching-bubbles.jpg" width="1080" height="610" layout="responsive" alt="A chameleon catching bubbles"></amp-img>
  27. <amp-img src="https://chameleon-catching-bubbles.jpg" width="1080" height="610" layout="responsive" alt="A chameleon catching bubbles"> <i-amp-sizer

    style="display: block; padding-top: 56.4815%;"></i-amp-sizer> <img alt="A chameleon catching bubbles" class="-amp-fill-content" src="https://chameleon-catching-bubbles.jpg"> </amp-img> REFLOW MANAGEMENT https://www.smashingmagazine.com/2016/08/ways-to-reduce-content-shifting-on-page-load/ AVOID REFLOWS TODAY
  28. <amp-img src="https://chameleon-catching-bubbles.jpg" width="1080" height="610" layout="responsive" alt="A chameleon catching bubbles"> <i-amp-sizer

    style="display: block; padding-top: 56.4815%;"></i-amp-sizer> <img alt="A chameleon catching bubbles" class="-amp-fill-content" src="https://chameleon-catching-bubbles.jpg"> </amp-img> REFLOW MANAGEMENT https://www.smashingmagazine.com/2016/08/ways-to-reduce-content-shifting-on-page-load/ AVOID REFLOWS TODAY
  29. INLINE CSS <style amp-custom> body { color: #404040; font-family: Helmet,Freesans,Helvetica,Arial,sans-serif;

    font-size: 14px; } .container { padding: 0 8px; } @media (min-width: 480px) { .container { padding: 0 16px; } } // // more things here // </style>
  30. CUSTOM FONTS @font-face { font-family: 'BioRhyme'; font-style: normal; font-weight: 400;

    src: local('BioRhyme'), local('BioRhyme-Regular'), url(https://some.font.url/font.woff2) format('woff2'); } "traditional" approach .custom-font-bold { font-family: 'BioRhyme', Arial, sans-serif; font-weight: 700; font-style: normal; } .custom-font { font-family: 'BioRhyme', Arial, sans-serif; font-weight: 400; font-style: normal; } @font-face { font-family: 'BioRhyme'; font-style: normal; font-weight: 700; src: local('BioRhyme Bold'), local('BioRhyme-Bold'), url(https://some.font.url/font-bold.woff2) format('woff2'); }
  31. "new" approach .custom-font { line-height: 1.3; letter-spacing: 2.675px; } .bio-rhyme-font-loaded

    .custom-font { font-family: 'BioRhyme', Arial, sans-serif; font-weight: 400; font-style: normal; letter-spacing: 0; line-height: 1.25; } CUSTOM FONTS https://ampbyexample.com/components/amp-font/
  32. "new" approach if (this.canUseNativeApis_()) { // Check if font already

    exists. if (this.document_.fonts.check(fontString)) { resolve(); } else { // Load font with native api if supported. this.document_.fonts.load(fontString).then(() => { // Workaround for chrome bug // https://bugs.chromium.org/p/chromium/issues/detail?id=347460 return this.document_.fonts.load(fontString); }).then(() => { if (this.document_.fonts.check(fontString)) { resolve(); } else { reject(new Error('Font could not be loaded,' + ' probably due to incorrect @font-face.')); } }).catch(reject); } } https://github.com/ampproject/amphtml/blob/master/extensions/amp-font/0.1/fontloader.js#L112-L131 CUSTOM FONTS
  33. "new" approach if (this.canUseNativeApis_()) { // Check if font already

    exists. if (this.document_.fonts.check(fontString)) { resolve(); } else { // Load font with native api if supported. this.document_.fonts.load(fontString).then(() => { // Workaround for chrome bug // https://bugs.chromium.org/p/chromium/issues/detail?id=347460 return this.document_.fonts.load(fontString); }).then(() => { if (this.document_.fonts.check(fontString)) { resolve(); } else { reject(new Error('Font could not be loaded,' + ' probably due to incorrect @font-face.')); } }).catch(reject); } } https://github.com/ampproject/amphtml/blob/master/extensions/amp-font/0.1/fontloader.js#L112-L131 CUSTOM FONTS
  34. "new" approach CUSTOM FONTS @font-face { font-family: 'BioRhyme'; font-display: swap;

    src: local('BioRhyme'), local('BioRhyme-Regular'), url(https://some.font.url/font.woff2) format('woff2'); } https://developers.google.com/web/updates/2016/02/font-display
  35. "new" approach CUSTOM FONTS @font-face { font-family: 'BioRhyme'; font-display: swap;

    src: local('BioRhyme'), local('BioRhyme-Regular'), url(https://some.font.url/font.woff2) format('woff2'); } https://developers.google.com/web/updates/2016/02/font-display
  36. FONT LOADING STRATEGY Rethink the usage of fonts! 01 Avoid

    FOIT! 02 03 Go for FOUT and adjust fallback fonts! https://www.zachleat.com/web/comprehensive-webfonts/
  37. GPU-ACCELERATED ANIMATIONS .sidebar { position: fixed; left: -300px; top: 0;

    bottom: 0; width: 300px; transition: left .5s ease-in-out; } .sidebar.is-open { left: 0; }
  38. <amp-sidebar id='sidebar1' layout='nodisplay'> <ul> <li> Nav item 1</li> <li> Nav

    item 2</li> <li> Nav item 3</li> </ul> </amp-sidebar> <button class="hamburger" on='tap:sidebar1.toggle'></button> GPU-ACCELERATED ANIMATIONS <script async custom-element="amp-sidebar" src="https://cdn.ampproject.org/v0/amp-sidebar-0.1.js"></script>
  39. GPU-ACCELERATED ANIMATIONS .sidebar { position: fixed; right: 100%; top: 0;

    bottom: 0; width: 300px; transform: translate( 0, 0 ); transition: transform .5s ease-in-out; will-change: transform; } .sidebar.is-open { transform: translate( 100%, 0 ); }
  40. GPU-ACCELERATED ANIMATIONS .sidebar { position: fixed; right: 100%; top: 0;

    bottom: 0; width: 300px; transform: translate( 0, 0 ); transition: transform .5s ease-in-out; will-change: transform; } .sidebar.is-open { transform: translate( 100%, 0 ); }
  41. FPS

  42. ANIMATIONS Use transform and opacity 01 Promote moving elements with

    'will-change' 02 03 Don't overdo it https://developers.google.com/web/fundamentals/.../stick-to-compositor-only-properties-and-manage-layer-count https://csstriggers.com/
  43. CSS CSS (gzipped) github.com ~109KB 23KB guardian.com ~295KB 46KB wired.com

    ~329KB 135KB dribbble.com ~385KB 68KB medium.com ~550KB 236KB m.facebook.com ~820KB 168KB mobile.nytimes.com ~900KB 189KB NOT INLINED CSS SHIPPED THESE DAYS (tested with iPhone 5 User agent)
  44. <script> (function() { var src = (document.location.protocol === 'https:' ?

    'https:/' : 'http:/') + '/imagesrv.adition.com/js/srp.js'; document.write('<scr' + 'ipt type="text/javascript" src="' + src + '" charset="utf-8"></scr' + 'ipt>'); })(); </script> document.write SCRIPT LOADING
  45. “We need to come up with a standard alternative real

    quick” Yoav Weiss Akamai https://twitter.com/yoavweiss/status/659141410990350336
  46. “I’ve been on the jQuery team for many years, and

    personally have seen how a library can drive meaningful, healthy change to the ecosystem at large. This is what we want.” Paul Bakaus Open Web Developer Advocate at Google https://medium.com/@pbakaus/doesnt-come-across-snarky-all-good-9d4269f7dbe6
  47. Feature Policy Ressource Limits CPU and Bandwidth Priority User Experience

    CONTENT PERFORMANCE POLICY http://wicg.github.io/ContentPerformancePolicy/
  48. https://wicg.github.io/feature-policy/ Feature-Policy: { "vibrate": ["self", "https://foo.com"], "sync-xhr": [], "usermedia": ["*"]

    } FEATURE POLICY <meta http-equiv="Feature-Policy" content='"vibrate": ["self", "https://foo.com"], "sync-xhr": []'> VIA HEADERS VIA META ELEMENT <iframe src="example.com" enable="vibrate" disable="sync-xhr"> VIA ATTRIBUTE Might change! currently in investigation
  49. CPU AND BANDWIDTH PRIORITY VIA META ELEMENT <meta http-equiv="cgroup" name="ads"

    content="cpu-share 0.2; cpu-priority medium; net-share 0.8; net-priority medium;"> <script cgroup="ads" src="/ads-manager.js" async></script> <iframe cgroup="ads" src="//3rdparty.com/widget" async></iframe> Might change! currently in investigation
  50. AMP CACHE & SEARCH How can it be so fast?

    https://cdn.ampproject.org/v/www.bbc.co.uk/news/amp/36202424 COMPLETE DOCUMENTS GO INTO AMP CACHE
  51. AMP CACHE & SEARCH How can it be so fast?

    <iframe class="u2quVpIWVr0__amp-doc" allowfullscreen="true" scrolling="no" src="https://cdn.ampproject.org/v/www.bbc.co.uk/news/amp/ 36202424? amp_js_v=5#dialog=0&prerenderSize=1&amp;visibilityState=hid den&amp;paddingTop=54&amp;history=1&amp;p2r=0&amp;horizonta lScrolling=0&amp;csi=0&amp;storage=1" style="height: 568px;"></iframe> SMART PRELOADING https://www.google.nl/amp/www.bbc.co.uk/...
  52. “If you try to convince somebody, that contents are worth

    their time, you don't want a loading bar to get in their way.” Anand Varma National Geographic