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

Making Modern Websites - Front-Trends 2016

Making Modern Websites - Front-Trends 2016

It seems like new APIs for the web are released every week. It would be cool to be able to use them, but if you need to make websites for the average user, you won't be able to use them for years – right? Wrong. Patrick will go over how creating feature rich and super fast front end applications, integrating over a dozen cutting edge web features, all while supporting even the oldest of web clients.

patrick kettner

May 20, 2016
Tweet

Other Decks in Technology

Transcript

  1. Clipboard API const Cross-document messaging Cross-Origin Resource Sharing crypto.getRandomValues() CSS

    Font Loading CSS.supports() API CustomEvent DeviceOrientation & DeviceMotion events Document Object Model Range DOM Parsing and Serialization ECMAScript 5 Element.getBoundingClientRect() Element.insertAdjacentHTML() ES6 Number Fetch FIDO U2F API File API KeyboardEvent.key KeyboardEvent.location MathML MP3 audio format MPEG-4/H.264 video format Node.textContent Ogg Vorbis audio format Ogg/Theora video format Opus PNG alpha transparency Public Key Pinning querySelector/querySelectorAll Referrer Policy Resource Hints: dns-prefetch Resource Hints: preconnect Resource Hints: prefetch Resource Hints: prerender WebGL - 3D Canvas graphics Offline web applications All HTML5 features Other AAC audio file format asm.js async attribute for external scripts autocomplete attribute: on & off values Brotli Accept-Encoding/Content-Encoding Client Hints: DPR, Width, Viewport-Width Content Security Policy 1.0 Content Security Policy Level 2 Data URIs defer attribute for external scripts document.head DOMContentLoaded ECMAScript 5 Strict Mode Minimum length attribute Multiple file selection New semantic elements Number input type Pattern attribute for input field Picture element PNG favicons progress element Range input type relList (DOMTokenList) Reversed attribute of ordered lis Ruby annotation sandbox attribute for iframes Scoped CSS seamless attribute for iframes Search input type Session history management Audio Tracks Autofocus attribute Canvas (basic support) Canvas blend modes classList (DOMTokenList) Color input type letter-spacing CSS property Media Queries: interaction media features Media Queries: resolution feature rem (root em) units text-decoration styling text-emphasis styling TTF/OTF Viewport units: vw, vh, vmin, vmax :placeholder-shown CSS pseudo-class Crisp edges/pixelated images Backdrop Filter Canvas Drawings Cross-Fade Function font-smooth image-set Logical Properties Motion Path pointer-events (for HTML) Background-image options Border images Border-radius (rounded corners) Box-shadow Box-sizing Colors Cursors (original values) Cursors: zoom-in & zoom-out font-kerning image-orientation Media Queries Multiple backgrounds Multiple column layout object-fit/object-position Opacity Overflow-wrap selectors tab-size font-stretch font-variant- alternates Generated content Gradients Grid Layout Hyphenation initial value inline-block ::first -letter CSS pseudo- element selecto r ::place holder CSS pseudo- element ::selec features SVG Inline SVG in HTML5 SVG (basic support) SVG effects for HTML SVG favicons SVG filters Internationalizatio API JSON parsing let matches() DOM metho matchMedia maxlength attribute input and textarea elements Media Source Extens Mutation Observer Navigation Timing A
  2. let font = new FontFaceObserver("T0t411y-R4D-Sans") font.load().then(() => { let body

    = document.body body.classList.toggle("fontzL0ad3d") }); @PatrickKettner
  3. let font = new FontFaceObserver("T0t411y-R4D-Sans") font.load().then(() => { let body

    = document.body body.classList.toggle("fontzL0ad3d") }); @PatrickKettner
  4. let font = new FontFaceObserver("T0t411y-R4D-Sans") font.load().then(() => { let body

    = document.body body.classList.toggle("fontzL0ad3d") }); @PatrickKettner
  5. let font = new FontFaceObserver("T0t411y-R4D-Sans") font.load().then(() => { let body

    = document.body body.classList.toggle("fontzL0ad3d") }); @PatrickKettner
  6. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  7. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  8. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  9. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  10. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  11. let CURRENT_CACHE = { prefetch: 'prefetch-cache-v1' }; self.addEventListener('install', event =>

    { let prefetch = ['/T0t411y-R4D-Sans'] event.waitUntil(caches.open(CURRENT_CACHE['prefetch']) .then(cache => cache.addAll(prefetch.map((url) => new Request(url, { 'mode': 'no-cors' }) )) ) ) }) @PatrickKettner
  12. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  13. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  14. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  15. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  16. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  17. self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request) .then((response) => { if (response)

    { return response; } return fetch(event.request) .then(response => response); })); }); @PatrickKettner
  18. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  19. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  20. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  21. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  22. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  23. if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

    window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); } } @PatrickKettner
  24. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  25. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  26. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  27. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  28. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  29. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  30. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  31. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  32. @PatrickKettner importScripts('/pako_deflate.js’, '/pretty-bytes.js'); onmessage = (msg, cb) => { let

    build = JSON.parse(msg.data).build; let response = { original: prettyBytes(build.length), compressed: prettyBytes(pako.deflate(build, { 'level’: 6 }).length) }; postMessage(response); }
  33. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  34. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  35. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  36. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  37. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  38. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  39. @PatrickKettner /build.js importScripts('/r.js', '/modernizr/build.js’); require(['build'],(builder) => { onmessage = (msg)

    => { let config = JSON.parse(msg.data).config; builder(config, postMessage); } });
  40. let content = build() ... zeroClipboard.on('copy', (e) => { let

    clipboard = e.clipboardData; clipboard.setData('text/plain', content); }); @PatrickKettner
  41. let content = build() ... zeroClipboard.on('copy', (e) => { let

    clipboard = e.clipboardData; clipboard.setData('text/plain', content); }); @PatrickKettner
  42. let content = build() ... zeroClipboard.on('copy', (e) => { let

    clipboard = e.clipboardData; clipboard.setData('text/plain', content); }); @PatrickKettner
  43. let content = build() ... zeroClipboard.on('copy', (e) => { let

    clipboard = e.clipboardData; clipboard.setData('text/plain', content); }); @PatrickKettner
  44. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  45. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  46. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  47. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  48. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  49. let content = build() ... let output = document.querySelector(’#output') output.innerHTML

    = content output.style.display = 'block' output.select() @PatrickKettner
  50. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  51. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  52. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  53. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  54. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  55. let content = build() let blob = new Blob([content], {

    type: 'text/plain' }); let href = URL.createObjectURL(blob) let download = 'modernizr.custom.js' @PatrickKettner
  56. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  57. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  58. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  59. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  60. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  61. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: { directory: { path: './dist' } } }]) server.start()
  62. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  63. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  64. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  65. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  66. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  67. let buildFromPostedQuery = function(request, reply) { let config = generateConfig(request.payload);

    Modernizr.build(config,(build) => { reply(build) .header('Content-Type', 'text/javascript') .header('Content-Disposition', 'attachment; filename=modernizr-custom.js') }) } @PatrickKettner
  68. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: {directory: {path: './dist'}} }, { method: 'POST', path: '/download', handler: buildFromPostedQuery }]) server.start()
  69. @PatrickKettner let server = new Hapi.Server() server.routes([{ method: 'GET', path:

    '/{param*}', handler: {directory: {path: './dist'}} }, { method: 'POST', path: '/download', handler: buildFromPostedQuery }]) server.start()
  70. * https://modernizr.com/download/?-csstransforms- csstransforms3d-csstransitions-svg !*/ !function(e,t,n){function r(e,t){return typeof e===t} function o(){var

    e,t,n,o,i,s,a;for(var l in y)if(y.hasOwnProperty(l)) {if(e=[],t=y[l],t.name&&(e.push(t.name.toLowerCase()),t.opti ons&&t.options.aliases&&t.options.aliases.length))for(n=0;n< t.options.aliases.length;n+ +)e.push(t.options.aliases[n].toLowerCase());for(o=r(t.fn,"f unction")?t.fn():t.fn,i=0;i<e.length;i+ +)s=e[i],a=s.split("."),1===a.length?Modernizr[a[0]]=o:(! Modernizr[a[0]]||Modernizr[a[0]]instanceof Boolean|| (Modernizr[a[0]]=new Boolean(Modernizr[a[0]])),Modernizr[a[0]] [a[1]]=o),C.push((o?"":"no-")+a.join("-"))}}function i(e) {var t = E.className,A @PatrickKettner
  71. * https://modernizr.com/download/?-csstransforms- csstransforms3d-csstransitions-svg !*/ !function(e,t,n){function r(e,t){return typeof e===t} function o(){var

    e,t,n,o,i,s,a;for(var l in y)if(y.hasOwnProperty(l)) {if(e=[],t=y[l],t.name&&(e.push(t.name.toLowerCase()),t.opti ons&&t.options.aliases&&t.options.aliases.length))for(n=0;n< t.options.aliases.length;n+ +)e.push(t.options.aliases[n].toLowerCase());for(o=r(t.fn,"f unction")?t.fn():t.fn,i=0;i<e.length;i+ +)s=e[i],a=s.split("."),1===a.length?Modernizr[a[0]]=o:(! Modernizr[a[0]]||Modernizr[a[0]]instanceof Boolean|| (Modernizr[a[0]]=new Boolean(Modernizr[a[0]])),Modernizr[a[0]] [a[1]]=o),C.push((o?"":"no-")+a.join("-"))}}function i(e) {var t = E.className,A @PatrickKettner
  72. let handler = (request, reply) => { if (userAgent.match(/bower/)) {

    buildForBower(config) } else if (userAgent.match(/npm/)) { buildForNPM(config) } else { reply.view('/download', config); } } @PatrickKettner
  73. let Archiver = require('archiver') let archive = Archiver('tar') let bowerJSON

    = makeBowerJSONFile() let buildForBower = (config) => { Modernizr.build(config, (build) => { var module = archive .append(build, {name: bowerJSON.main}) .append(JSON.stringify(bowerJSON), {name: 'bower.json'}) .finalize(); reply(module) .header('Content-disposition', 'attachment; filename=Modernizr.custom.tar') }) } @PatrickKettner
  74. let Archiver = require('archiver') let archive = Archiver('tar') let pkg

    = makePackageJSON() let buildForNPM = (config) => { Modernizr.build(config, (build) => { var module = archive .append(build, {name: pkgJSON.main}) .append(JSON.stringify(pkgJSON), {name: package.json'}) .finalize(); reply(module) .header('Content-disposition', 'attachment; filename=Modernizr.custom.tar') }) } @PatrickKettner