KEMBAR78
Going web native | PDF
G O I N G W E B N AT I V E
T H E E X C I T I N G F U T U R E O F W E B
@ M A R C U S H E L L B E R G
5
S T O R Y A N D P H I L O S O P H Y
Software is eating the world and what most of us see of it is the user interface. The user
interface has become the key component of how the users experience the business
behind it. Competition is lost or won due to user experience. Simplicity is king and the
users get frustrated by anything ugly, slow or not working on the device they happen to
use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight.
Together we want to build a user interface that puts a smile on the user’s face.
Vaadin is the technology that empowers developers to build the best web-apps for
business purposes. Our priority over everything else is developer productivity because
we believe that by simplifying the developer experience and saving the developer’s
time, they are best able to focus on building great user interfaces.
Our brand is what we want everyone to think about us. When everyone - both us and
the people around us - have a consistent understanding of what Vaadin is and what we
stand for, it enables that image to spread and amplify. This book defines what we want
that image to be. It defines what the Vaadin brand is.
I hope that You are as excited and proud of living and breathing the Vaadin brand as
I am. You are the one who is shaping what everyone thinks about Vaadin - using this
brand as a tool and a guideline every day.
Let’s fight for simplicity for both the users and the developers!
Joonas Lehtinen
Founder & CEO
Vaadin
I N T R O D U C T I O N
FIGHT FOR SIMPLICITY
FIGHT FOR SIMPLICITY
•HOW WE GOT TO WHERE WE ARE
•WHAT THE WEB PLATFORM CAN DO TODAY
•PREDICTING THE FUTURE
T H E H I S T O R Y O F
B U I L D I N G W E B A P P L I C AT I O N S
CLIENTSERVER
Model
View
Controller
CLIENTSERVER
Model
View
Controller
CLIENTSERVER
Model
View
Controller
CLIENTSERVER
Model
View
Controller
CLIENTSERVER
Model
View
Controller
CLIENTSERVER
REST
View
Model
Controller
CLIENTSERVER
REST
View
Model
Controller
CLIENTSERVER
REST
View
Model
Controller
CLIENTSERVER
REST
View
Model
Controller
CLIENTSERVER
REST
View
Model
Controller
CLIENTSERVER
REST
Component1
Component 2
Component 4REST
REST
CLIENTSERVER
REST
Component1
Component 2
Component 4REST
REST
CLIENTSERVER
REST
Component1
Component 2
Component 4REST
REST
CLIENTSERVER
REST
Component1
Component 2
Component 4REST
REST
B U I L D I N G C O M P O N E N T S
<input type="text" id="datepicker">
STEP1:
<input type="text" id="datepicker">
STEP1:
I'm a text input!
<input type="text" id="datepicker">
STEP1:
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
STEP2:
I'm a text input!
<input type="text" id="datepicker">
STEP1:
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
STEP2:
I'm a text input!
<script>
$( function() {
$( "#datepicker" ).datepicker();
} );
</script>
STEP3:
<input type="text" id="datepicker">
STEP1:
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq
<link rel="stylesheet" href="/resources/demos/style.css">
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
STEP2:
<script>
$( function() {
$( "#datepicker" ).datepicker();
} );
</script>
STEP3:
What kind of freak am I ?!?
5 MINUTES LATER...
•Depends on a library
•JavaScript scoping issues
•CSS scoping issues
•A big bowl of spaghetti™
@Component({
selector: 'demo-component',
template: `
<h1>Hello {{user}}!</h1>
`,
styles: [`
h1 {
color: red;
}
`]
})
export class DemoComponent {
public user: String;
constructor() {
this.user = 'Marcus';
}
}
@Component({
selector: 'demo-component',
template: `
<h1>Hello {{user}}!</h1>
`,
styles: [`
h1 {
color: red;
}
`]
})
export class DemoComponent {
public user: String;
constructor() {
this.user = 'Marcus';
}
}
<demo-component>
</demo-component>
•Custom element with sensible API
•Encapsulates JavaScript and CSS
•Bound to one specific framework
•Needs to be "emulated" in the browser
P R O B L E M :
R E N D E R I S J S B L O C K E D
53% of mobile site visits are
abandoned if pages take
longer than 3 seconds to load
”
W E B C O M P O N E N T S
<web-component></web-component>
Custom Element
<web-component></web-component>
Custom Element
(needs to have a dash in the name)
<web-component></web-component>
HTML Import
<link rel="import" src="web-component.html">
<template id="web-component">
<web-component></web-component>
</template>
Template
<web-component>
# shadow root (open)
<style>
.danger { color: red; }
</style>
<span class="danger">
Alert!
</span>
</web-component>
Shadow root
B U I L D I N G A C O M P O N E N T
class CollapseLayout extends HTMLElement {
}
class CollapseLayout extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: 'open' });
}
}
class CollapseLayoutVanilla extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: 'open' });
this.root.innerHTML = `
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open]{ display: block; }
</style>
<div id="title">
<slot name="title"></slot>
</div>
<div id="content">
<slot></slot>
</div>
`;
}
}
class CollapseLayoutVanilla extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: 'open' });
this.root.innerHTML = `
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open]{ display: block; }
</style>
<div id="title">
<slot name="title"></slot>
</div>
<div id="content">
<slot></slot>
</div>
`;
}
}
class CollapseLayoutVanilla extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: 'open' });
this.root.innerHTML = `
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open]{ display: block; }
</style>
<div id="title">
<slot name="title"></slot>
</div>
<div id="content">
<slot></slot>
</div>
`;
}
}
class CollapseLayout extends HTMLElement {
constructor() {
super();
this.root = this.attachShadow({ mode: 'open' });
this.root.innerHTML = `
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open]{ display: block; }
</style>
<div id="title">
<slot name="title"></slot>
</div>
<div id="content">
<slot></slot>
</div>
`;
this.addEventListener('click', this.toggleOpen);
}
}
class CollapseLayout extends HTMLElement {
constructor() {}
toggleOpen() { this.setOpen(!this.open); }
setOpen(open) {
if (open) {
this.setAttribute('open', open);
this.root.querySelector('#content').setAttribute('open', open);
} else {
this.removeAttribute('open');
this.root.querySelector('#content').removeAttribute('open');
}
}
get open() { return this.hasAttribute('open'); }
}
class CollapseLayout extends HTMLElement {
constructor() { }
toggleOpen() { }
setOpen(open) { }
get open() { }
static get observedAttributes() { return ['open']; }
attributeChangedCallback(name, oldValue, newValue) {
if ('open' === name && this.open !== !!newValue) {
this.toggleOpen();
}
}
}
class CollapseLayout extends HTMLElement {
constructor() { }
toggleOpen() { }
setOpen(open) { }
get open() { }
static get observedAttributes() { }
attributeChangedCallback(name, oldValue, newValue) { }
}
customElements.define('collapse-layout', CollapseLayout);
<collapse-layout>
<h1 slot="title">Custom Element Title</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
elit. Ut erat nisi, placerat eget lorem a, fermentum cong
convallis mi. Mauris neque elit, pretium vitae metus sed,
ac lacinia. Phasellus lobortis vitae mauris ullamcorper p
</p>
</collapse-layout>
<collapse-layout>
<h1 slot="title">Custom Element Title</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
elit. Ut erat nisi, placerat eget lorem a, fermentum cong
convallis mi. Mauris neque elit, pretium vitae metus sed,
ac lacinia. Phasellus lobortis vitae mauris ullamcorper p
</p>
</collapse-layout>
<collapse-layout open>
<h1 slot="title">Custom Element Title</h1>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
elit. Ut erat nisi, placerat eget lorem a, fermentum cong
convallis mi. Mauris neque elit, pretium vitae metus sed,
ac lacinia. Phasellus lobortis vitae mauris ullamcorper p
</p>
</collapse-layout>
Polymer
<dom-module id="collapse-layout">
</dom-module>
<dom-module id="collapse-layout">
<script>
class CollapseLayout extends Polymer.Element {
}
</script>
</dom-module>
<dom-module id="collapse-layout">
<script>
class CollapseLayout extends Polymer.Element {
static get is() { return 'collapse-layout'; }
}
</script>
</dom-module>
<dom-module id="collapse-layout">
<script>
class CollapseLayout extends Polymer.Element {
static get is() { return 'collapse-layout'; }
static get properties() {
return {
open: {
type: Boolean,
reflectToAttribute: true
}
}
}
}
</script>
</dom-module>
<dom-module id="collapse-layout">
<script>
class CollapseLayout extends Polymer.Element {
static get is() { return 'collapse-layout'; }
static get properties() {
return {
open: {
type: Boolean,
reflectToAttribute: true
}
}
}
toggleOpen() { this.open = !this.open; }
}
</script>
</dom-module>
<dom-module id="collapse-layout">
<script>
class CollapseLayout extends Polymer.Element {
static get is() { return 'collapse-layout'; }
static get properties() {
return {
open: {
type: Boolean,
reflectToAttribute: true
}
}
}
toggleOpen() { this.open = !this.open; }
customElements.define(CollapseLayout.is, CollapseLayout);
}
</script>
</dom-module>
<dom-module id="collapse-layout">
<template>
</template>
<script>...</script>
</dom-module>
<dom-module id="collapse-layout">
<template>
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open] { display: block; }
</style>
<div id="title">
<slot name="title" on-click="toggleOpen"></slot>
</div>
<div id="content" open$="[[open]]">
<slot></slot>
</div>
</template>
<script></script>
</dom-module>
<dom-module id="collapse-layout">
<template>
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open] { display: block; }
</style>
<div id="title">
<slot name="title" on-click="toggleOpen"></slot>
</div>
<div id="content" open$="[[open]]">
<slot></slot>
</div>
</template>
<script></script>
</dom-module>
<dom-module id="collapse-layout">
<template>
<style>
#title { cursor: pointer; }
#content { display: none; }
#content[open] { display: block; }
</style>
<div id="title">
<slot name="title" on-click="toggleOpen"></slot>
</div>
<div id="content" open$="[[open]]">
<slot></slot>
</div>
</template>
<script></script>
</dom-module>
•Simplified element creation
•Data binding
•Event handling
•Repeating and conditional templates
•CLI tool for bootstrapping and building projects
•Set of basic UI components
P O L Y M E R I S W E B C O M P O N E N T S +
vaadin.com/elements
@addyosmani
aerotwist.com/blog/when-everything-is-important-nothing-is
CLIENT SIDE RENDERING
aerotwist.com/blog/when-everything-is-important-nothing-is
SERVER SIDE RENDERING
aerotwist.com/blog/when-everything-is-important-nothing-is
PROGRESSIVE BOOTING
P R P L
P R P LP u s h
P R P LR e n d e rP u s h
P R P LP r e - c a c h eR e n d e rP u s h
P R P LP r e - c a c h eR e n d e rP u s h L a z y - l o a d
C O M P O N E N T S O N T H E W E B
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
2013
Polymer
React
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
2013
Polymer
React
2014
Vue
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
2013
Polymer
React
2015
Angular2
2014
Vue
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
2013
Polymer
React
2015
Angular2
2014
Vue
2016
Polymer 2
A N O N - E X H A U S T I V E H I S T O R Y
C O M P O N E N T S O N T H E W E B2011
Web Components
2013
Polymer
React
2015
Angular2
2014
Vue
2016
Polymer 2
2017
Web Components widely supported
A N O N - E X H A U S T I V E H I S T O R Y
M O B I L E
E X P E R I E N C E
Mobile now represents almost 2 out of 3
digital media minutes.
comscore.com/Insights/Presentations-and-Whitepapers/2016/The-2016-US-Mobile-App-Report
A P P I N S TA L L S / M O N T H :
A P P I N S TA L L S / M O N T H :
0
While we haven’t yet reached ‘Peak App’ the market
is definitely tightening, and app publishers need to
rethink how to break through to the consumer’s
screen.
comScore 2016 US Mobile App report
comscore.com/Insights/Presentations-and-Whitepapers/2016/The-2016-US-Mobile-App-Report
gartner.com/smarterwithgartner/gartner-predicts-2017-marketers-expect-the-unexpected
GARTNER: 20% WILL ABANDON THEIR
MOBILE APPS BY 2019
Despite significant investment and hopes for positive ROI,
mobile applications are not paying off for many
brands.
Compelling alternatives such as progressive web apps
mean the branded app economy is poised for change.
gartner.com/smarterwithgartner/gartner-predicts-2017-marketers-expect-the-unexpected
GARTNER: 20% WILL ABANDON THEIR
MOBILE APPS BY 2019
T H E P R O B L E M I S F R I C T I O N
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
find your app in the store64
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
find your app in the store64
tap install51
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
find your app in the store64
tap install51
accept permission requests41
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
find your app in the store64
tap install51
accept permission requests41
find the app on their home screen33
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
open the app store80
find your app in the store64
tap install51
accept permission requests41
find the app on their home screen33
26 will open the app
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
tap install80
accept permission requests64
find the app on their home screen51
41will open the app
S T I L L , O N L Y . . .
C O S T P E R I N S TA L L
$ 1 . 5 0 +
fiksu.com/resources/fiksu-indexes
Users spend almost
90% of time in their top
5 apps.
Source: comScore 2016 report
W H AT A B O U T M O B I L E W E B ?
80 will open the app
O U T O F 1 0 0 I N T E R E S T E D P E O P L E
Source: comScore 2016 report
0
3
6
9
12
# Visitors (MM)
Native apps Mobile web
Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
Source: comScore 2016 report
0
3
6
9
12
# Visitors (MM)
3x
Native apps Mobile web
Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
Source: comScore 2016 report
0
3
6
9
12
# Visitors (MM)
0
50
100
150
200
Minutes
3x 20x
Native apps Mobile web
Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
WHAT'S HOLDING BACK THE
MOBILE WEB?
1 . B A D U S E R E X P E R I E N C E
2 . P O O R R E - E N G A G E M E N T
Your t-shirt order has shipped. Track it here.
5
S T O R Y A N D P H I L O S O P H Y
Software is eating the world and what most of us see of it is the user interface. The user
interface has become the key component of how the users experience the business
behind it. Competition is lost or won due to user experience. Simplicity is king and the
users get frustrated by anything ugly, slow or not working on the device they happen to
use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight.
Together we want to build a user interface that puts a smile on the user’s face.
Vaadin is the technology that empowers developers to build the best web-apps for
business purposes. Our priority over everything else is developer productivity because
we believe that by simplifying the developer experience and saving the developer’s
time, they are best able to focus on building great user interfaces.
Our brand is what we want everyone to think about us. When everyone - both us and
the people around us - have a consistent understanding of what Vaadin is and what we
stand for, it enables that image to spread and amplify. This book defines what we want
that image to be. It defines what the Vaadin brand is.
I hope that You are as excited and proud of living and breathing the Vaadin brand as
I am. You are the one who is shaping what everyone thinks about Vaadin - using this
brand as a tool and a guideline every day.
Let’s fight for simplicity for both the users and the developers!
Joonas Lehtinen
Founder & CEO
Vaadin
I N T R O D U C T I O N
Vaadin Store
3 . P E R F O R M A N C E
P R O G R E S S I V E W E B A P P S
1. Reliable
1. Reliable
2. Fast
1. Reliable
2. Fast
3. Engaging
T R Y B E F O R E Y O U B U Y
P W A C H E C K L I S T :
Responsive layouts, works on mobile
Site works cross-browser
Page transitions don't feel like they block on network
Each page has a URL
developers.google.com/web/progressive-web-apps/checklist
P W A C H E C K L I S T :
Site is served over HTTPS
Responsive layouts, works on mobile
Site works cross-browser
Page transitions don't feel like they block on network
Each page has a URL
developers.google.com/web/progressive-web-apps/checklist
P W A C H E C K L I S T :
Site is served over HTTPS
Responsive layouts, works on mobile
Site works cross-browser
Page transitions don't feel like they block on network
Each page has a URL
Metadata is provided for Add to Home screen
developers.google.com/web/progressive-web-apps/checklist
P W A C H E C K L I S T :
Site is served over HTTPS
Responsive layouts, works on mobile
First load is fast even on 3G
Site works cross-browser
Page transitions don't feel like they block on network
Each page has a URL
Metadata is provided for Add to Home screen
developers.google.com/web/progressive-web-apps/checklist
D O Y O U N E E D T O S TA R T F R O M
S C R AT C H ?
D O Y O U N E E D T O S TA R T F R O M
S C R AT C H ?
N O .
1 . A D D A N A P P M A N I F E S T
{
"name": "Todo App",
"icons": [{
"src": "todo.png",
"sizes": "192x192",
"type": "image/png"
}],
"start_url": "/index.html",
"display": "standalone",
"orientation": "portrait"
}
https://developer.mozilla.org/en-US/docs/Web/Manifest
W E B A P P M A N I F E S T
<head>
...
<link rel="manifest" href="/manifest.webmanifest">
</head>
2 . D E F I N E A S E R V I C E W O R K E R
Service worker

S E R V I C E W O R K E R
if ('serviceWorker' in navigator) {
}
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js');
});
}
let CACHE_NAME = 'cache-v1';
let urlsToCache = ['/', '/styles/main.css',
'/script/main.js'];
/sw.js
let CACHE_NAME = 'cache-v1';
let urlsToCache = ['/', '/styles/main.css',
'/script/main.js'];
self.addEventListener('install', event => {
});
let CACHE_NAME = 'cache-v1';
let urlsToCache = ['/', '/styles/main.css',
'/script/main.js'];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME));
});
let CACHE_NAME = 'cache-v1';
let urlsToCache = ['/', '/styles/main.css',
'/script/main.js'];
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache));
);
});
self.addEventListener('fetch', event => {
});
self.addEventListener('fetch', event => {
event.respondWith(
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
);
});
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
github.com/GoogleChrome/sw-toolbox
T O O L S
github.com/GoogleChrome/sw-toolbox
toolbox.router.get('*.json', toolbox.cacheFirst);->
T O O L S
github.com/GoogleChrome/sw-precache
github.com/GoogleChrome/sw-toolbox
toolbox.router.get('*.json', toolbox.cacheFirst);->
T O O L S
3 . A D D P U S H , B A C K G R O U N D S Y N C
( I F Y O U W A N T T O )
developers.google.com/web/fundamentals/
getting-started/codelabs/push-notifications
You can progressively enhance your existing app
to become a Progressive Web App.
Taking an app designed for desktop and slapping
on a ServiceWorker won't make it a PWA.
Lighthouse
T H E B A D N E W S
N O I O S S U P P O R T
N O I O S S U P P O R T
(YET)
T H E G O O D N E W S
G R E AT A N D R O I D S U P P O R T
G R E AT A N D R O I D S U P P O R T
(88% MARKET SHARE)
A N D D E S K T O P
T H E Y A R E A P R O G R E S S I V E
E N H A N C E M E N T
C A N B E U S E D W I T H A N Y
F R A M E W O R K
P A R T O F T H E W E B P L AT F O R M
W H AT ' S A H E A D ?
THE WEB IS THE NEXT BIG
PLATFORM FOR APPS
HARDWARE ACCESS
A SAMPLE OF INTERESTING WEB SPECS
Web Bluetooth
Device motion
Ambient light sensor
Vibration
BROWSER
Background sync
Payment API
Share API
Credentials API
Persistent storage
GAMING
Web Assembly
WebGL
WebVR
NATIVE ISN'T GOING AWAY
NATIVE ISN'T GOING AWAY
BUT IT WILL BECOME MORE NICHE
medium.com/@owencm/reactive-web-design-the-secret-
to-building-web-apps-that-feel-amazing-b5cbfe9b7c50
BORROWING IDEAS
FRAMEWORKS WILL BECOME LIGHTER AND
MORE MODULAR
PHYSICAL WEB WILL MEAN MORE
SINGLE-USE APPS
Reaching the next generation of web users will
require performance and bandwidth consciousness
Reaching the next generation of web users will
require performance and bandwidth consciousness
Native Lite
Example:
INDIA
Reaching the next generation of web users will
require performance and bandwidth consciousness
216MB/month
Native Lite
Example:
INDIA
Reaching the next generation of web users will
require performance and bandwidth consciousness
8.6MB/month216MB/month
Native Lite
Example:
INDIA
Reaching the next generation of web users will
require performance and bandwidth consciousness
8.6MB/month216MB/month
= 7.3h work
Native Lite
Example:
INDIA
Reaching the next generation of web users will
require performance and bandwidth consciousness
8.6MB/month216MB/month
= 7.3h work = 0.5h work
Native Lite
Example:
INDIA
Data sync is the next JS framework war
– Alex Russell”
F U R T H E R R E A D I N G
aerotwist.com/blog/the-cost-of-frameworks
aerotwist.com/blog/when-everything-is-important-nothing-is
medium.com/@marcushellberg/simplifying-performance-with-web-components-7d5327314b69
infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul
developers.google.com/web/fundamentals
P E O P L E T O F O L L O W
( O N S O C I A L M E D I A , Y O U C R E E P )
@slightlylate
Alex Russell
@aerotwist
Paul Lewis
@nolanlawson
Nolan Lawson
@jaffathecake
Jake Archibald
@owencrm
Owen Campbell-Moore
I’ve found it’s been a better long-term
investment for me to learn the Web Platform
than any particular library, framework or tool
– Paul Lewis
”
D I S C U S S I O N
T H A N K Y O U !
@ M A R C U S H E L L B E R G
5
S T O R Y A N D P H I L O S O P H Y
Software is eating the world and what most of us see of it is the user interface. The user
interface has become the key component of how the users experience the business
behind it. Competition is lost or won due to user experience. Simplicity is king and the
users get frustrated by anything ugly, slow or not working on the device they happen to
use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight.
Together we want to build a user interface that puts a smile on the user’s face.
Vaadin is the technology that empowers developers to build the best web-apps for
business purposes. Our priority over everything else is developer productivity because
we believe that by simplifying the developer experience and saving the developer’s
time, they are best able to focus on building great user interfaces.
Our brand is what we want everyone to think about us. When everyone - both us and
the people around us - have a consistent understanding of what Vaadin is and what we
stand for, it enables that image to spread and amplify. This book defines what we want
that image to be. It defines what the Vaadin brand is.
I hope that You are as excited and proud of living and breathing the Vaadin brand as
I am. You are the one who is shaping what everyone thinks about Vaadin - using this
brand as a tool and a guideline every day.
Let’s fight for simplicity for both the users and the developers!
Joonas Lehtinen
Founder & CEO
Vaadin
I N T R O D U C T I O N

Going web native

  • 1.
    G O IN G W E B N AT I V E T H E E X C I T I N G F U T U R E O F W E B @ M A R C U S H E L L B E R G 5 S T O R Y A N D P H I L O S O P H Y Software is eating the world and what most of us see of it is the user interface. The user interface has become the key component of how the users experience the business behind it. Competition is lost or won due to user experience. Simplicity is king and the users get frustrated by anything ugly, slow or not working on the device they happen to use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight. Together we want to build a user interface that puts a smile on the user’s face. Vaadin is the technology that empowers developers to build the best web-apps for business purposes. Our priority over everything else is developer productivity because we believe that by simplifying the developer experience and saving the developer’s time, they are best able to focus on building great user interfaces. Our brand is what we want everyone to think about us. When everyone - both us and the people around us - have a consistent understanding of what Vaadin is and what we stand for, it enables that image to spread and amplify. This book defines what we want that image to be. It defines what the Vaadin brand is. I hope that You are as excited and proud of living and breathing the Vaadin brand as I am. You are the one who is shaping what everyone thinks about Vaadin - using this brand as a tool and a guideline every day. Let’s fight for simplicity for both the users and the developers! Joonas Lehtinen Founder & CEO Vaadin I N T R O D U C T I O N
  • 4.
  • 5.
  • 7.
    •HOW WE GOTTO WHERE WE ARE •WHAT THE WEB PLATFORM CAN DO TODAY •PREDICTING THE FUTURE
  • 8.
    T H EH I S T O R Y O F B U I L D I N G W E B A P P L I C AT I O N S
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
    B U IL D I N G C O M P O N E N T S
  • 24.
  • 25.
  • 26.
    <input type="text" id="datepicker"> STEP1: <linkrel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq <link rel="stylesheet" href="/resources/demos/style.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> STEP2: I'm a text input!
  • 27.
    <input type="text" id="datepicker"> STEP1: <linkrel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq <link rel="stylesheet" href="/resources/demos/style.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> STEP2: I'm a text input! <script> $( function() { $( "#datepicker" ).datepicker(); } ); </script> STEP3:
  • 28.
    <input type="text" id="datepicker"> STEP1: <linkrel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jq <link rel="stylesheet" href="/resources/demos/style.css"> <script src="https://code.jquery.com/jquery-1.12.4.js"></script> <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script> STEP2: <script> $( function() { $( "#datepicker" ).datepicker(); } ); </script> STEP3: What kind of freak am I ?!?
  • 29.
  • 31.
    •Depends on alibrary •JavaScript scoping issues •CSS scoping issues •A big bowl of spaghetti™
  • 33.
    @Component({ selector: 'demo-component', template: ` <h1>Hello{{user}}!</h1> `, styles: [` h1 { color: red; } `] }) export class DemoComponent { public user: String; constructor() { this.user = 'Marcus'; } }
  • 34.
    @Component({ selector: 'demo-component', template: ` <h1>Hello{{user}}!</h1> `, styles: [` h1 { color: red; } `] }) export class DemoComponent { public user: String; constructor() { this.user = 'Marcus'; } } <demo-component> </demo-component>
  • 35.
    •Custom element withsensible API •Encapsulates JavaScript and CSS •Bound to one specific framework •Needs to be "emulated" in the browser
  • 36.
    P R OB L E M : R E N D E R I S J S B L O C K E D
  • 38.
    53% of mobilesite visits are abandoned if pages take longer than 3 seconds to load ”
  • 39.
    W E BC O M P O N E N T S
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
    <web-component> # shadow root(open) <style> .danger { color: red; } </style> <span class="danger"> Alert! </span> </web-component> Shadow root
  • 45.
    B U IL D I N G A C O M P O N E N T
  • 48.
  • 49.
    class CollapseLayout extendsHTMLElement { constructor() { super(); this.root = this.attachShadow({ mode: 'open' }); } }
  • 50.
    class CollapseLayoutVanilla extendsHTMLElement { constructor() { super(); this.root = this.attachShadow({ mode: 'open' }); this.root.innerHTML = ` <style> #title { cursor: pointer; } #content { display: none; } #content[open]{ display: block; } </style> <div id="title"> <slot name="title"></slot> </div> <div id="content"> <slot></slot> </div> `; } }
  • 51.
    class CollapseLayoutVanilla extendsHTMLElement { constructor() { super(); this.root = this.attachShadow({ mode: 'open' }); this.root.innerHTML = ` <style> #title { cursor: pointer; } #content { display: none; } #content[open]{ display: block; } </style> <div id="title"> <slot name="title"></slot> </div> <div id="content"> <slot></slot> </div> `; } }
  • 52.
    class CollapseLayoutVanilla extendsHTMLElement { constructor() { super(); this.root = this.attachShadow({ mode: 'open' }); this.root.innerHTML = ` <style> #title { cursor: pointer; } #content { display: none; } #content[open]{ display: block; } </style> <div id="title"> <slot name="title"></slot> </div> <div id="content"> <slot></slot> </div> `; } }
  • 53.
    class CollapseLayout extendsHTMLElement { constructor() { super(); this.root = this.attachShadow({ mode: 'open' }); this.root.innerHTML = ` <style> #title { cursor: pointer; } #content { display: none; } #content[open]{ display: block; } </style> <div id="title"> <slot name="title"></slot> </div> <div id="content"> <slot></slot> </div> `; this.addEventListener('click', this.toggleOpen); } }
  • 54.
    class CollapseLayout extendsHTMLElement { constructor() {} toggleOpen() { this.setOpen(!this.open); } setOpen(open) { if (open) { this.setAttribute('open', open); this.root.querySelector('#content').setAttribute('open', open); } else { this.removeAttribute('open'); this.root.querySelector('#content').removeAttribute('open'); } } get open() { return this.hasAttribute('open'); } }
  • 55.
    class CollapseLayout extendsHTMLElement { constructor() { } toggleOpen() { } setOpen(open) { } get open() { } static get observedAttributes() { return ['open']; } attributeChangedCallback(name, oldValue, newValue) { if ('open' === name && this.open !== !!newValue) { this.toggleOpen(); } } }
  • 56.
    class CollapseLayout extendsHTMLElement { constructor() { } toggleOpen() { } setOpen(open) { } get open() { } static get observedAttributes() { } attributeChangedCallback(name, oldValue, newValue) { } } customElements.define('collapse-layout', CollapseLayout);
  • 57.
    <collapse-layout> <h1 slot="title">Custom ElementTitle</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. elit. Ut erat nisi, placerat eget lorem a, fermentum cong convallis mi. Mauris neque elit, pretium vitae metus sed, ac lacinia. Phasellus lobortis vitae mauris ullamcorper p </p> </collapse-layout>
  • 58.
    <collapse-layout> <h1 slot="title">Custom ElementTitle</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. elit. Ut erat nisi, placerat eget lorem a, fermentum cong convallis mi. Mauris neque elit, pretium vitae metus sed, ac lacinia. Phasellus lobortis vitae mauris ullamcorper p </p> </collapse-layout>
  • 59.
    <collapse-layout open> <h1 slot="title">CustomElement Title</h1> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. elit. Ut erat nisi, placerat eget lorem a, fermentum cong convallis mi. Mauris neque elit, pretium vitae metus sed, ac lacinia. Phasellus lobortis vitae mauris ullamcorper p </p> </collapse-layout>
  • 60.
  • 61.
  • 62.
    <dom-module id="collapse-layout"> <script> class CollapseLayoutextends Polymer.Element { } </script> </dom-module>
  • 63.
    <dom-module id="collapse-layout"> <script> class CollapseLayoutextends Polymer.Element { static get is() { return 'collapse-layout'; } } </script> </dom-module>
  • 64.
    <dom-module id="collapse-layout"> <script> class CollapseLayoutextends Polymer.Element { static get is() { return 'collapse-layout'; } static get properties() { return { open: { type: Boolean, reflectToAttribute: true } } } } </script> </dom-module>
  • 65.
    <dom-module id="collapse-layout"> <script> class CollapseLayoutextends Polymer.Element { static get is() { return 'collapse-layout'; } static get properties() { return { open: { type: Boolean, reflectToAttribute: true } } } toggleOpen() { this.open = !this.open; } } </script> </dom-module>
  • 66.
    <dom-module id="collapse-layout"> <script> class CollapseLayoutextends Polymer.Element { static get is() { return 'collapse-layout'; } static get properties() { return { open: { type: Boolean, reflectToAttribute: true } } } toggleOpen() { this.open = !this.open; } customElements.define(CollapseLayout.is, CollapseLayout); } </script> </dom-module>
  • 67.
  • 68.
    <dom-module id="collapse-layout"> <template> <style> #title {cursor: pointer; } #content { display: none; } #content[open] { display: block; } </style> <div id="title"> <slot name="title" on-click="toggleOpen"></slot> </div> <div id="content" open$="[[open]]"> <slot></slot> </div> </template> <script></script> </dom-module>
  • 69.
    <dom-module id="collapse-layout"> <template> <style> #title {cursor: pointer; } #content { display: none; } #content[open] { display: block; } </style> <div id="title"> <slot name="title" on-click="toggleOpen"></slot> </div> <div id="content" open$="[[open]]"> <slot></slot> </div> </template> <script></script> </dom-module>
  • 70.
    <dom-module id="collapse-layout"> <template> <style> #title {cursor: pointer; } #content { display: none; } #content[open] { display: block; } </style> <div id="title"> <slot name="title" on-click="toggleOpen"></slot> </div> <div id="content" open$="[[open]]"> <slot></slot> </div> </template> <script></script> </dom-module>
  • 71.
    •Simplified element creation •Databinding •Event handling •Repeating and conditional templates •CLI tool for bootstrapping and building projects •Set of basic UI components P O L Y M E R I S W E B C O M P O N E N T S +
  • 73.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
    P R PLP u s h
  • 82.
    P R PLR e n d e rP u s h
  • 83.
    P R PLP r e - c a c h eR e n d e rP u s h
  • 84.
    P R PLP r e - c a c h eR e n d e rP u s h L a z y - l o a d
  • 85.
    C O MP O N E N T S O N T H E W E B A N O N - E X H A U S T I V E H I S T O R Y
  • 86.
    C O MP O N E N T S O N T H E W E B2011 Web Components A N O N - E X H A U S T I V E H I S T O R Y
  • 87.
    C O MP O N E N T S O N T H E W E B2011 Web Components 2013 Polymer React A N O N - E X H A U S T I V E H I S T O R Y
  • 88.
    C O MP O N E N T S O N T H E W E B2011 Web Components 2013 Polymer React 2014 Vue A N O N - E X H A U S T I V E H I S T O R Y
  • 89.
    C O MP O N E N T S O N T H E W E B2011 Web Components 2013 Polymer React 2015 Angular2 2014 Vue A N O N - E X H A U S T I V E H I S T O R Y
  • 90.
    C O MP O N E N T S O N T H E W E B2011 Web Components 2013 Polymer React 2015 Angular2 2014 Vue 2016 Polymer 2 A N O N - E X H A U S T I V E H I S T O R Y
  • 91.
    C O MP O N E N T S O N T H E W E B2011 Web Components 2013 Polymer React 2015 Angular2 2014 Vue 2016 Polymer 2 2017 Web Components widely supported A N O N - E X H A U S T I V E H I S T O R Y
  • 92.
    M O BI L E E X P E R I E N C E
  • 93.
    Mobile now representsalmost 2 out of 3 digital media minutes. comscore.com/Insights/Presentations-and-Whitepapers/2016/The-2016-US-Mobile-App-Report
  • 95.
    A P PI N S TA L L S / M O N T H :
  • 96.
    A P PI N S TA L L S / M O N T H : 0
  • 97.
    While we haven’tyet reached ‘Peak App’ the market is definitely tightening, and app publishers need to rethink how to break through to the consumer’s screen. comScore 2016 US Mobile App report comscore.com/Insights/Presentations-and-Whitepapers/2016/The-2016-US-Mobile-App-Report
  • 98.
  • 99.
    Despite significant investmentand hopes for positive ROI, mobile applications are not paying off for many brands. Compelling alternatives such as progressive web apps mean the branded app economy is poised for change. gartner.com/smarterwithgartner/gartner-predicts-2017-marketers-expect-the-unexpected GARTNER: 20% WILL ABANDON THEIR MOBILE APPS BY 2019
  • 100.
    T H EP R O B L E M I S F R I C T I O N
  • 101.
    O U TO F 1 0 0 I N T E R E S T E D P E O P L E
  • 102.
    open the appstore80 O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 103.
    open the appstore80 find your app in the store64 O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 104.
    open the appstore80 find your app in the store64 tap install51 O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 105.
    open the appstore80 find your app in the store64 tap install51 accept permission requests41 O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 106.
    open the appstore80 find your app in the store64 tap install51 accept permission requests41 find the app on their home screen33 O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 107.
    open the appstore80 find your app in the store64 tap install51 accept permission requests41 find the app on their home screen33 26 will open the app O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 109.
    tap install80 accept permissionrequests64 find the app on their home screen51 41will open the app S T I L L , O N L Y . . .
  • 110.
    C O ST P E R I N S TA L L $ 1 . 5 0 + fiksu.com/resources/fiksu-indexes
  • 111.
    Users spend almost 90%of time in their top 5 apps. Source: comScore 2016 report
  • 112.
    W H ATA B O U T M O B I L E W E B ?
  • 114.
    80 will openthe app O U T O F 1 0 0 I N T E R E S T E D P E O P L E
  • 115.
    Source: comScore 2016report 0 3 6 9 12 # Visitors (MM) Native apps Mobile web Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
  • 116.
    Source: comScore 2016report 0 3 6 9 12 # Visitors (MM) 3x Native apps Mobile web Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
  • 117.
    Source: comScore 2016report 0 3 6 9 12 # Visitors (MM) 0 50 100 150 200 Minutes 3x 20x Native apps Mobile web Top 1000 Mobile Apps vs. Top 1000 Mobile Web Properties
  • 118.
    WHAT'S HOLDING BACKTHE MOBILE WEB?
  • 119.
    1 . BA D U S E R E X P E R I E N C E
  • 123.
    2 . PO O R R E - E N G A G E M E N T
  • 126.
    Your t-shirt orderhas shipped. Track it here. 5 S T O R Y A N D P H I L O S O P H Y Software is eating the world and what most of us see of it is the user interface. The user interface has become the key component of how the users experience the business behind it. Competition is lost or won due to user experience. Simplicity is king and the users get frustrated by anything ugly, slow or not working on the device they happen to use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight. Together we want to build a user interface that puts a smile on the user’s face. Vaadin is the technology that empowers developers to build the best web-apps for business purposes. Our priority over everything else is developer productivity because we believe that by simplifying the developer experience and saving the developer’s time, they are best able to focus on building great user interfaces. Our brand is what we want everyone to think about us. When everyone - both us and the people around us - have a consistent understanding of what Vaadin is and what we stand for, it enables that image to spread and amplify. This book defines what we want that image to be. It defines what the Vaadin brand is. I hope that You are as excited and proud of living and breathing the Vaadin brand as I am. You are the one who is shaping what everyone thinks about Vaadin - using this brand as a tool and a guideline every day. Let’s fight for simplicity for both the users and the developers! Joonas Lehtinen Founder & CEO Vaadin I N T R O D U C T I O N Vaadin Store
  • 127.
    3 . PE R F O R M A N C E
  • 128.
    P R OG R E S S I V E W E B A P P S
  • 129.
  • 130.
  • 131.
  • 132.
    T R YB E F O R E Y O U B U Y
  • 137.
    P W AC H E C K L I S T : Responsive layouts, works on mobile Site works cross-browser Page transitions don't feel like they block on network Each page has a URL developers.google.com/web/progressive-web-apps/checklist
  • 138.
    P W AC H E C K L I S T : Site is served over HTTPS Responsive layouts, works on mobile Site works cross-browser Page transitions don't feel like they block on network Each page has a URL developers.google.com/web/progressive-web-apps/checklist
  • 139.
    P W AC H E C K L I S T : Site is served over HTTPS Responsive layouts, works on mobile Site works cross-browser Page transitions don't feel like they block on network Each page has a URL Metadata is provided for Add to Home screen developers.google.com/web/progressive-web-apps/checklist
  • 140.
    P W AC H E C K L I S T : Site is served over HTTPS Responsive layouts, works on mobile First load is fast even on 3G Site works cross-browser Page transitions don't feel like they block on network Each page has a URL Metadata is provided for Add to Home screen developers.google.com/web/progressive-web-apps/checklist
  • 141.
    D O YO U N E E D T O S TA R T F R O M S C R AT C H ?
  • 142.
    D O YO U N E E D T O S TA R T F R O M S C R AT C H ? N O .
  • 143.
    1 . AD D A N A P P M A N I F E S T
  • 144.
    { "name": "Todo App", "icons":[{ "src": "todo.png", "sizes": "192x192", "type": "image/png" }], "start_url": "/index.html", "display": "standalone", "orientation": "portrait" } https://developer.mozilla.org/en-US/docs/Web/Manifest W E B A P P M A N I F E S T
  • 145.
  • 146.
    2 . DE F I N E A S E R V I C E W O R K E R
  • 147.
    Service worker  S ER V I C E W O R K E R
  • 148.
    if ('serviceWorker' innavigator) { }
  • 149.
    if ('serviceWorker' innavigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js'); }); }
  • 150.
    let CACHE_NAME ='cache-v1'; let urlsToCache = ['/', '/styles/main.css', '/script/main.js']; /sw.js
  • 151.
    let CACHE_NAME ='cache-v1'; let urlsToCache = ['/', '/styles/main.css', '/script/main.js']; self.addEventListener('install', event => { });
  • 152.
    let CACHE_NAME ='cache-v1'; let urlsToCache = ['/', '/styles/main.css', '/script/main.js']; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME)); });
  • 153.
    let CACHE_NAME ='cache-v1'; let urlsToCache = ['/', '/styles/main.css', '/script/main.js']; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(urlsToCache)); ); });
  • 154.
  • 155.
    self.addEventListener('fetch', event =>{ event.respondWith( ); });
  • 156.
    self.addEventListener('fetch', event =>{ event.respondWith( caches.match(event.request) ); });
  • 157.
    self.addEventListener('fetch', event =>{ event.respondWith( caches.match(event.request) .then(response => { if (response) { return response; } return fetch(event.request); } ) ); });
  • 158.
  • 159.
  • 160.
  • 161.
    3 . AD D P U S H , B A C K G R O U N D S Y N C ( I F Y O U W A N T T O )
  • 162.
  • 163.
    You can progressivelyenhance your existing app to become a Progressive Web App. Taking an app designed for desktop and slapping on a ServiceWorker won't make it a PWA.
  • 164.
  • 165.
    T H EB A D N E W S
  • 166.
    N O IO S S U P P O R T
  • 167.
    N O IO S S U P P O R T (YET)
  • 168.
    T H EG O O D N E W S
  • 169.
    G R EAT A N D R O I D S U P P O R T
  • 170.
    G R EAT A N D R O I D S U P P O R T (88% MARKET SHARE)
  • 171.
    A N DD E S K T O P
  • 172.
    T H EY A R E A P R O G R E S S I V E E N H A N C E M E N T
  • 173.
    C A NB E U S E D W I T H A N Y F R A M E W O R K P A R T O F T H E W E B P L AT F O R M
  • 175.
    W H AT' S A H E A D ?
  • 176.
    THE WEB ISTHE NEXT BIG PLATFORM FOR APPS
  • 177.
    HARDWARE ACCESS A SAMPLEOF INTERESTING WEB SPECS Web Bluetooth Device motion Ambient light sensor Vibration BROWSER Background sync Payment API Share API Credentials API Persistent storage GAMING Web Assembly WebGL WebVR
  • 178.
  • 179.
    NATIVE ISN'T GOINGAWAY BUT IT WILL BECOME MORE NICHE
  • 180.
  • 181.
    FRAMEWORKS WILL BECOMELIGHTER AND MORE MODULAR
  • 182.
    PHYSICAL WEB WILLMEAN MORE SINGLE-USE APPS
  • 183.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness
  • 184.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness Native Lite Example: INDIA
  • 185.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness 216MB/month Native Lite Example: INDIA
  • 186.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness 8.6MB/month216MB/month Native Lite Example: INDIA
  • 187.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness 8.6MB/month216MB/month = 7.3h work Native Lite Example: INDIA
  • 188.
    Reaching the nextgeneration of web users will require performance and bandwidth consciousness 8.6MB/month216MB/month = 7.3h work = 0.5h work Native Lite Example: INDIA
  • 189.
    Data sync isthe next JS framework war – Alex Russell”
  • 190.
    F U RT H E R R E A D I N G aerotwist.com/blog/the-cost-of-frameworks aerotwist.com/blog/when-everything-is-important-nothing-is medium.com/@marcushellberg/simplifying-performance-with-web-components-7d5327314b69 infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul developers.google.com/web/fundamentals
  • 191.
    P E OP L E T O F O L L O W ( O N S O C I A L M E D I A , Y O U C R E E P ) @slightlylate Alex Russell @aerotwist Paul Lewis @nolanlawson Nolan Lawson @jaffathecake Jake Archibald @owencrm Owen Campbell-Moore
  • 192.
    I’ve found it’sbeen a better long-term investment for me to learn the Web Platform than any particular library, framework or tool – Paul Lewis ”
  • 193.
    D I SC U S S I O N
  • 194.
    T H AN K Y O U ! @ M A R C U S H E L L B E R G 5 S T O R Y A N D P H I L O S O P H Y Software is eating the world and what most of us see of it is the user interface. The user interface has become the key component of how the users experience the business behind it. Competition is lost or won due to user experience. Simplicity is king and the users get frustrated by anything ugly, slow or not working on the device they happen to use at the time. We at Vaadin fight for simplicity and invite everyone to join this fight. Together we want to build a user interface that puts a smile on the user’s face. Vaadin is the technology that empowers developers to build the best web-apps for business purposes. Our priority over everything else is developer productivity because we believe that by simplifying the developer experience and saving the developer’s time, they are best able to focus on building great user interfaces. Our brand is what we want everyone to think about us. When everyone - both us and the people around us - have a consistent understanding of what Vaadin is and what we stand for, it enables that image to spread and amplify. This book defines what we want that image to be. It defines what the Vaadin brand is. I hope that You are as excited and proud of living and breathing the Vaadin brand as I am. You are the one who is shaping what everyone thinks about Vaadin - using this brand as a tool and a guideline every day. Let’s fight for simplicity for both the users and the developers! Joonas Lehtinen Founder & CEO Vaadin I N T R O D U C T I O N