Making Modern Websites

Post on 19-Feb-2017

277 views 0 download

transcript

Making Modern WebsitesPatrick Kettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

Websites .

@PatrickKettner

Making Websites .

@PatrickKettner

Making Websites is Hard.

@PatrickKettner

srsly.

@PatrickKettner

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

FileReader API

Full Screen API

Gamepad API

Geolocation

getUserMedia/Stream API

Hashchange event

High Resolution Time API

IndexedDB

Input Method Editor 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

Server Name Indication

Shadow DOM

Strict Transport Security

Upgrade Insecure Requests

WAI-ARIA Accessibility features

Wav audio format

WebM video format

Window.devicePixelRatio

WOFF - Web Open Font Format

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

Element.closest()

EventTarget.addEventListener()

EventTarget.dispatchEvent

getComputedStyle

HTTP/2 protocol

JPEG 2000 image format

JPEG XR image format

KeyboardEvent.code

KeyboardEvent.getModifierState()

meter element

Minimum length attribute

Multiple file selection

New semantic elements

Number input type

Pattern attribute for input fields

Picture element

PNG favicons

progress element

Range input type

relList (DOMTokenList)

Reversed attribute of ordered lists

Ruby annotation

sandbox attribute for iframes

Scoped CSS

seamless attribute for iframes

Search input type

Session history management

Spellcheck attribute

srcdoc attribute for iframes

Srcset attribute

Subresource Integrity

Text API for Canvas

Toolbar/context menu

Video element

Video Tracks

wbr (word break opportunity) element

Audio Tracks

Autofocus attribute

Canvas (basic support)

Canvas blend modes

classList (DOMTokenList)

Color input type

contenteditable attribute

Custom Elements

Custom protocol handling

Datalist element

dataset & data-* attributes

Date and time input types

Details & Summary elements

Dialog element

disabled attribute

Download attribute

Drag and Drop

Email, telephone & URL input types

Form attribute

Form validation

getElementsByClassName

hidden attribute

HTML Imports

HTML templates

HTML5 form features

input event

input placeholder attribute

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)

position:sticky

Reflections

text-size-adjust

text-stroke

zoom

Improved kerning pairs & ligatures

All CSS features

accept attribute for file input

Audio element

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

text-align-last

Text-overflow

Text-shadow

Transitions

word-break

OM Scroll-behavior

Flexible Box Layout Module

Font unicode-range subsetting

Intrinsic & Extrinsic Sizing

font-stretch

font-variant-alternates

Generated content

Gradients

Grid Layout

Hyphenation

initial value

inline-block

Masks

min/max-width/height

outline

page-break properties

position:fixed

Regions

Repeating Gradients

resize property

Scroll snap points

Shapes Level 1

Table display

touch-action property

unset value

user-select: none

Variables

will-change property

writing-mode property

2D Transforms

3D Transforms

::first-letter CSS pseudo-element selector

::placeholder CSS pseudo-element

::selection CSS pseudo-element

@font-face Web fonts

Blending of HTML/SVG elements

calc() as CSS unit value

2.1 selectors

all property

Animation

Appearance

background-attachment

background-blend-mode

background-position edge offsets

box-decoration-break

clip-path property

Counter Styles

Counters

currentColor value

Device Adaptation

element() function

Exclusions Level 1

Feature Queries

Filter Effects

filter() function

font-feature-settings

font-size-adjust

WOFF 2.0 - Web Open Font Format

XHTML served as application/xhtml+xml

Animated PNG (APNG)

EOT - Embedded OpenType fonts

KeyboardEvent.charCode

KeyboardEvent.which

Node.innerText

Resource Hints: Lazyload

SPDY protocol

WebP image format

WebVTT - Web Video Text Tracks

XHTML+SMIL animation

All Other features

SVG

Inline SVG in HTML5

SVG (basic support)

SVG effects for HTML

SVG favicons

SVG filters

SVG fonts

SVG fragment identifiers

SVG in CSS backgrounds

SVG in HTML img element

SVG SMIL animation

All SVG features

JS API

Ambient Light API

Arrow functions

Base64 encoding and decoding

Battery Status API

Beacon API

Blob constructing

Blob URLs

BroadcastChannel

Channel messaging

Internationalization API

JSON parsing

let

matches() DOM method

matchMedia

maxlength attribute for input and textarea elements

Media Source Extensions

Mutation Observer

Navigation Timing API

Object RTC (ORTC) API for WebRTC

Online/offline status

Page Visibility

PageTransitionEvent

Pointer events

PointerLock API

Promises

Proximity API

Proxy object

requestAnimationFrame

Resource Timing

Rest parameters

Screen Orientation

Server-sent events

Service Workers

Shared Web Workers

Touch events

Typed Arrays

User Timing API

Vibration API

Web Animations API

Web Audio API

Web Cryptography

Web MIDI API

Web Notifications

Web Sockets

Web Storage - name/value pairs

Web Workers

WebRTC Peer-to-peer connections

XMLHttpRequest advanced features

Basic console logging functions

Document.execCommand()

Efficient Script Yielding: setImmediate()

Filesystem & FileWriter API

Network Information API

Object.observe data binding

Permissions API

Speech Recognition API

Speech Synthesis API

Web SQL Database

@PatrickKettner

@PatrickKettner

tools .

@PatrickKettner

@PatrickKettner

@PatrickKettner

Second most used lib

@PatrickKettner

@PatrickKettner

@PatrickKettner

commit a9a90192ea11dc0ff916431b40227734af9074b6

Author: Patrick Kettner <patrickkettner@gmail.com>

Date: Fri Sep 11 18:59:17 2015 -0700

release 3.0

@PatrickKettner

@PatrickKettner

{{months of work}}

@PatrickKettner

@PatrickKettner

no one cares.

@PatrickKettner

(other than me)

@PatrickKettner

at best,we don’t get in the way

@PatrickKettner

we are just a pitstop

@PatrickKettner

we still need tomake users happy

@PatrickKettner

how?

@PatrickKettner

make itreally, really fast

@PatrickKettner

@PatrickKettner

@PatrickKettner

@joecritchley

@PatrickKettner

@font-face

@PatrickKettner

@PatrickKettner

@PatrickKettner

the site works, itjust looks broken

@PatrickKettner

@PatrickKettner

document.fonts.ready.then(() => { let body = document.body body.classList.toggle("fontzL0ad3d")});

@PatrickKettner

body { font-family: 'T0t411y-R4D-Sans', 'Helvetica', 'Sans-Serif'}

@PatrickKettner

body { font-family: 'Helvetica', 'Sans-Serif'}

body.fontzL0ad3d { font-family: 'T0t411y-R4D-Sans'}

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

FontFaceObserver

@PatrickKettner

github.com/bramstein/fontfaceobserver

document.fonts.ready.then(() => { let body = document.body body.classList.toggle("fontzL0ad3d")});

@PatrickKettner

@PatrickKettner

let observer = new FontFaceObserver("T0t411y-R4D-Sans");

observer.check().then(() => { let body = document.body body.classList.toggle("fontzL0ad3d")});

@PatrickKettner

@PatrickKettner

December 1, 2015@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

observer.check().then(() => { localStorage.setItem("fontzL0ad3d", true)});

if it is cached properly

@PatrickKettner

and the user didn’t clear part of the cache

@PatrickKettner

and the browser didn’t automatically remove it

@PatrickKettner

this was alwaysan educated guess

@PatrickKettner

ServiceWorker

@PatrickKettner

kindof a big deal...

@PatrickKettner

@PatrickKettner

Offline ExperiencesPush NotificationsBackground Syncand much, much more...

@PatrickKettner

FetchCache

- Network Proxy- Programmable Cache

@PatrickKettner

259 modules

@PatrickKettner

single module =dozens of requests

@PatrickKettner

ServiceWorker?

@PatrickKettner

navigator.serviceWorker.register('/serviceworker.js')

@PatrickKettner

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

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

@PatrickKettner

@PatrickKettner

:(

@PatrickKettner

hero

@PatrickKettner

savior

@PatrickKettner

douchebag

@PatrickKettner

AppCache

@PatrickKettner

@JaffaTheCake

AppCache

@PatrickKettner

ServiceWorker > AppCache

@PatrickKettner

@PatrickKettner

...but it covers our use

@PatrickKettner

CACHE MANIFEST

#__CACHE_VERSION__

//download/css/main.css/img/logo.svg

/js/build.js

__ASSETS__

NETWORK:*

@PatrickKettner

<html lang="en" manifest="/offline.appcache"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> ...

@PatrickKettner

<html lang="en" manifest="/offline.appcache"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> ...

@PatrickKettner

<html lang="en" manifest="/offline.appcache"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> ...

@PatrickKettner

ServiceWorker > AppCache

@PatrickKettner

if('serviceWorker' in navigator) { navigator.serviceWorker.register('/serviceworker.js')} else if ('applicationCache' in window) { // add AppCache}

@PatrickKettner

can’t be added by js

@PatrickKettner

let iframe = document.createElement('iframe')iframe.style.display = 'none'iframe.src = '/load-appcache.html'document.body.appendChild(iframe)

@PatrickKettner

<html manifest="/offline.appcache"> <head> <title>loading douchebags</title> </head> <body></body></html>

@PatrickKettner

20.7 seconds

@PatrickKettner

190 milliseconds

@PatrickKettner

ServiceWorker > AppCache

@PatrickKettner

ServiceWorkers!

@PatrickKettner

ServiceWorkers

@PatrickKettner

WebWorkers

@PatrickKettner

commit 90b52e847359ae902d3f7ce7bc511cadfbc29ea8

Author: Alexey Proskuryakov <ap@webkit.org>

Date: Thu Nov 6 07:04:47 +0000

Implement Worker global object

@PatrickKettner

2008!

@PatrickKettner

Version 2@PatrickKettner

Version 2@PatrickKettner

WebWorkers

@PatrickKettner

WebWorkers?

@PatrickKettner

single threadedby default

@PatrickKettner

everything is fighting for CPU time

@PatrickKettner

lots of number crunchin’===

hardcore jank

@PatrickKettner

Offload tasks to background thread

@PatrickKettner

super expensive fnsbecome pretty cheap

@PatrickKettner

dynamic filesize calculation

@PatrickKettner

@PatrickKettner

100% clientside!

@PatrickKettner

if ('Worker' in window) { let gzipWorker = new Worker('/gzip.js');

window.gziper = (config, cb) => { gzipWorker.postMessage(config); gzipWorker.onmessage = (e) => cb(e.data); }}

@PatrickKettner

@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);}

@PatrickKettner

/downloadif ('Worker' in window) { window.buildWorker = new Worker('/build.js');}

@PatrickKettner

/downloadbuildWorker.postMessage(JSON.stringify({config}));

@PatrickKettner

@PatrickKettner

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

by the time BUILD is clickedits already built

@PatrickKettner

“what if their browser don’t have workers”

@PatrickKettner

@PatrickKettner

¯\_(ツ )_/¯ nbd

@PatrickKettner

we’ll just build then

@PatrickKettner

@PatrickKettner

@PatrickKettner

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

@PatrickKettner

@PatrickKettner

:(

@PatrickKettner

:(

@PatrickKettner

@PatrickKettner

if (Modernizr.flash) { // ZeroClipboard} else { // somethingElse}

somethingElse?

@PatrickKettner

@PatrickKettner

let content = build()...let output = document.querySelector('output')output.innerHTML = buildoutput.style.display = 'block'output.select()

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

Blobs, URL, & [download]

@PatrickKettner

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

@PatrickKettner

@PatrickKettner

<a href={href} download={download}>DOWNLOAD</a>

@PatrickKettner

@PatrickKettner

@PatrickKettner

@PatrickKettner

~ 61% :/

@PatrickKettner

@PatrickKettner

@PatrickKettner

Just POST It

@PatrickKettner

@PatrickKettner

<a href={href} download={download}>DOWNLOAD</a>

@PatrickKettner

if (supportsEurythang) { download = <a href={href} download={download}>DOWNLOAD</a>} else { download = <input type='submit' value='download'/>}

Decent Markup

@PatrickKettner

@PatrickKettner

CSS

@PatrickKettner

CSS

@PatrickKettner

JavaScript

@PatrickKettner

JavaScript

@PatrickKettner

100% clientside!

@PatrickKettner

100% clientside!unless it can’t

@PatrickKettner

Server Side

@PatrickKettner

100% static files

@PatrickKettner

@PatrickKettner

let server = new Hapi.Server()

server.routes([{ method: 'GET', path: '/{param*}', handler: { directory: { path: './dist' } }}])

server.start()

99.999% static files

@PatrickKettner

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

@PatrickKettner

let server = new Hapi.Server()

server.routes([{ method: 'GET', path: '/{param*}', handler: {directory: {path: './dist'}}}, { method: 'POST', path: '/download', handler: buildFromPostedQuery}])

server.start()

works for all browsers!

@PatrickKettner

srsly.

@PatrickKettner

@PatrickKettner

@PatrickKettner

HTML is cool!

@PatrickKettner

no one cares.

@PatrickKettner

@PatrickKettner

@PatrickKettner

“I want to bower install!”

@PatrickKettner

“I want to npm install!”

@PatrickKettner

“There isn’t a file to install”

@PatrickKettner

“...”

@PatrickKettner

“I want to npm install!”

@PatrickKettner

no one cares.

@PatrickKettner

no one cares.

@PatrickKettner

can’t publish every combo

@PatrickKettner

259 modules

@PatrickKettner

259 ^ 259

@PatrickKettner

3.2317 x 10616

@PatrickKettner

no one cares.

@PatrickKettner

Server already buildsfor anything POSTed

@PatrickKettner

/*! modernizr 3.2.0 (Custom Build) | MIT * * 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.options&&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,"function")?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

/*! modernizr 3.2.0 (Custom Build) | MIT * * 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.options&&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,"function")?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

npm install --save https://modernizr.com/download/?-csstransforms-csstransforms3d-csstransitions-svg

@PatrickKettner

let handler = (request, reply) => { if (userAgent.match(/bower/)) { buildForBower(config) } else if (userAgent.match(/npm/)) { buildForNPM(config) } else { reply.view('/download', config); }}

@PatrickKettner

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

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

“wait…require(‘moderznir’)?”

@PatrickKettner

@PatrickKettner

require(‘moderznir’) !== modernizr

@PatrickKettner

@PatrickKettner

Modernizr.build(config, (build) => { fs.writeFileSync('./modernizr.js’)}

very flexible

@PatrickKettner

not super friendly

@PatrickKettner

we already have ainterface we like...

@PatrickKettner

lets reuse it!

@PatrickKettner

React is cool!

@PatrickKettner

no one cares.

@PatrickKettner

get out of the way,in the best ways possible

@PatrickKettner

make awesome stuff.

@PatrickKettner

use the new shiny.

@PatrickKettner

have a lot of fun.

@PatrickKettner

Thanks!

@PatrickKettner