I have a
confession
to make
“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
“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
graph TD;
web[http://example.com];
html[HTML Content];
web-->html;
html-- Link -->web;
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?
- Initial page load speed
- Crawlability and curlability
- More accessible by default
- Forces inclusive design
When is
progressive enhancement
non-negotiable?
- Your site is content-driven & publicly available
- You’re building a government site
- Your site is an “essential service”, i.e. Coles Online
- Long-lived applications
- Richer, more app-like experiences
- Fluid transitions between multiple states
- Singular, cohesive, testable UI codebase
- Easier to build, test and reason about
- Can be packaged into an app, e.g. PhoneGap
Browserify
James Halliday, a.k.a. Substack
Airbnb, 2013
+
React
— The promise of —
Universal JavaScript
Dawn
of the
Progressive
Single Page App
The
elements
— of —
progressive
single page apps
- Rendering
- Routing
- Data fetching
- Form submissions
- Micro-interactions
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>
Client rendering is
progressive enhancement
React.render(<MyComponent />, element);
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);
});
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
// 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} />
}
};
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
// 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);
<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
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;
.Header__Menu__Panel { transform: translateX(-500px) }
.Header__Menu__Toggle:checked ~
.Header__Menu__Panel { transform: none }
CSS
=
Magic global strings
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
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
The future
— belongs to —
Rich, progressive apps
Looking to get started with progressive React?
@markdalgleish