I have a

confession

to make

I broke the web

“Nobody disables JavaScript!”

“All your users are non-JS
while they’re downloading your JS”

Jake Archibald, Google

Your first load is

your first impression

“Time to first tweet”

“Make sure your web design adheres to the principles of progressive enhancement as this helps our systems (and a wider range of browsers) see usable content and basic functionality”

Google Webmaster Central

Progressive

Enhancement

— circ. 2003 —

“…all the way up from “baseline”, to “legacy”, to “midrange”, to “modern” and beyond.”

Steve Champeon

“Build in layers, from HTML to CSS to Javascript. This creates a less fragile and more foolproof Web page”

Luke Wroblewski

“Progressive enhancement becomes an important strategy in order to deal with today and tomorrow’s diverse landscape.”

Brad Frost

“Fundamentally, progressive
enhancement is about accessibility”

Aaron Gustafson

Building with

— the web’s —

Foundational elements

HTTP 0.9

— circ. 1991 —

graph TD;
  web[http://example.com];
  html[HTML Content];
  web-->html;
  html-- Link -->web;
  

Until…

The Ajax Revolution

— circ. 2005 —

graph TD;
  web[http://example.com];
  api[http://api.example.com];
  js[JavaScript];
  web-- HTML Transport -->js;
  js-- AJAX -->api;
  api-->js;
  

Progressive

enhancement

— vs. —

single page

apps?

Why

progressive

enhancement?

When is

progressive enhancement

non-negotiable?

Why

single page

apps?

Something changed

Node.js

Ryan Dahl

Browserify

James Halliday, a.k.a. Substack

Airbnb, 2013

Rendr

+

Backbone
React

React

— The promise of —

Universal JavaScript

Dawn

of the

Progressive

Single Page App

How?

The

elements

— of —

progressive

single page apps

i.

Rendering

Server Rendering

React

React

import React, { Component } from 'react';

export default class MyComponent extends Component {
  render() {
    return <div>Hello World!</div>;
  }
}
// Server
React.renderToString(<MyComponent />);
<div id="app"
  data-reactid=".23qqlyvw6io"
  data-react-checksum="509597258">
  
  <div data-reactid=".23qqlyvw6io.0" >Hello World!</div>
  
</div>
<script src="my-awesome-app.js"></script>

Internet like it’s 1999

Client rendering is

progressive enhancement

React.render(<MyComponent />, element);

ii.

Routing

react-router

Ryan Florence & Michael Jackson

import React from 'react';
import { Route } from 'react-router';

export default (
  <Route name="root" path="/" handler={RootHandler}>
    <Route name="results" handler={ResultsHandler} />
  </Route>
);
app.get('*', conn => serverRender(
  conn.path,
  conn.request.headers.Cookie
));
Router.run(routes, path, Handler => {
  resolve(`<html>
             <div id="app">
               ${ React.renderToString(<Handler />) }
             </div>
           </html>`);
});
// Client router callback
Router.run(routes, HistoryLocation, Handler => {
  React.render(<Handler />, element);
});

iii.

Data fetching

graph TD;
  show_spinner[Show spinner];
  fetch_content[Fetch content];
  show_content[Show content];
  hide_spinner[Hide spinner];
  show_spinner-->fetch_content;
  fetch_content-->show_content;
  fetch_content-->hide_spinner;
  

React.renderToString(    );

// Smarter server rendering:

FETCH_ALL_THE_THINGS(routeHandlers)
  .then(data => {
    React.renderToString(<App data={data} />);
  });
  

Decorate Route Handlers

— & —

Fetch on demand

react-fetcher

// index.js
import fetchData from './lib/fetchData';
import Fetch from './lib/Fetch';

export default {
  fetchData,
  Fetch
};

// fetchData.js
import Promise from 'bluebird';

export default (routes, ...args) => {
  const promises = routes
    // Get route fetchers
    .map(route => route.handler.fetchers)
    
    // Filter out missing fetchers
    .filter(fetchers => fetchers)
    
    // Ensure fetchers are in an array
    .map(fetchers => Array.isArray(fetchers) ? fetchers : [fetchers])
    
    // Generate an array of fetcher promises
    .map(fetchers => Promise.all(fetchers.map(fetcher => fetcher(...args))));
    
  return Promise.all(promises);
};

// Fetch.js
import React from 'react';

export default (fetchers) => (ComposedComponent) => class extends React.Component {
  static fetchers = fetchers;
  
  render() {
    return <ComposedComponent {...this.props} />
  }
};

@Fetch

Proposal by Yehuda Katz

// Fetch decorator
import { Fetch } from 'react-fetcher';

@Fetch(({ params }) => getData(params.id))
class MyComponent extends Component {
  render() { ... }
}

Fetch functions

— are —

Async functions

fetchData(handlers)

// Server
import { fetchData } from 'react-fetcher';

fetchData(handlers, { ...args }).then((data) => {
  React.render(<App data={data} />, element);
});
// Client
React.render(<App data={???} />, element);
<script>
  window.FETCHED_DATA =
    ${ JSON.stringify(fetchedData) };
</script>
// Initial client render
if (window.FLUX_DATA) {
  flux.deserialize(window.FLUX_DATA);
  delete window.FLUX_DATA;
}

React.render(<Handler flux={flux} />, el);

iv.

Form submissions

The Zombie Form

<form method="get"
      action="/results"
      onSubmit={this.onSubmit.bind(this)} >
      

/results?keywords=javascript

<input type=“hidden” ... />
onSubmit(event) {
  event.preventDefault();
  // ...
}

First Build Forms

Without JS

v.

Micro-interactions

The Zombie Button

No JavaScript?

Checkbox hacks!

(Thanks Ryan Seddon)

.menu { transform: translateX(-500px) }

.toggle:checked ~ .menu { transform: none }

Maintainable checkbox hacks?

Component Based Architecture

graph TD;
  js[JavaScript Component];
  css[Locally scoped CSS];
  cssdep[CSS Dependency];
  images[Images];
  imagesdep[Images];
  fontsdep[Fonts];
  js-->css;
  css-->images;
  css-->cssdep;
  cssdep-->imagesdep;
  cssdep-->fontsdep;
  

Webpack

.Header__Menu__Panel { transform: translateX(-500px) }

.Header__Menu__Toggle:checked ~
  .Header__Menu__Panel { transform: none }
  

CSS

=

Magic global strings

CSS is a code smell

github.com/css-modules

import styles from './RefineBar.less';

// locally scoped!
// no more magic strings!
<div className={styles.root}>
  <div className={styles.summary}>
    ...
  </div>
</div>
.menu { transform: translateX(-500px) }

.toggle:checked ~ .menu { transform: none }

“The End of Global CSS”

SEEK UI Engineering Blog

Project Holy Grail

Demo time

Coming soon…

The future?

Progressive Enhancement

+

Progressive Apps

“…client-side frameworks that have server-rendering as an option work well with the model of [...] Progressive Apps”

Alex Russell, Google

Beyond React…

virtual-dom

Ember’s

Glimmer

&

FastBoot

The future

— belongs to —

Rich, progressive apps

Let’s fix the web

Looking to get started with progressive React?

@markdalgleish