Web Components

A presentation at @ Fondsnet in February 2016 in Berlin, Germany by Gunnar Bittersmann

Slide 1

Slide 1

Web Components

Slide 2

Slide 2

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world

Slide 3

Slide 3

<body> HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </body>

Slide 4

Slide 4

<body> <main> HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </main> </body>

Slide 5

Slide 5

<body> <main> <h1> ✘ HURRICANE </h1> by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </main> </body>

Slide 6

Slide 6

<body> <main> <h1> Hurricane </h1> by Bob Dylan and Jacques Levy h1 { Here comes the story of the Hurricane text-transform:The uppercase; man the authorities came to blame } For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </main> </body>

Slide 7

Slide 7

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> Hurricane A footer typically contains information Here comes the story of the Hurricane about its section as who wrote it, The man the authorities came tosuch blame documents, copyright For somethin’links that to he related never done data, the like. Put in a prison cell,and but one time he could-a been The champion of the world </main> </body>

Slide 8

Slide 8

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> Hurricane footer Here comes the story of the Hurricane { The man the authorities came to blame font-style: italic; For somethin’ that he never done } Put in a prison cell, but one time he could-a been The champion of the world </main> </body>

Slide 9

Slide 9

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </p> </main> </body> Hurricane

Slide 10

Slide 10

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> Here comes the story of the Hurricane <br/> The man the authorities came to blame <br/> For somethin’ that he never done <br/> Put in a prison cell, but one time he could-a been <br/> The champion of the world </p> </main> </body> Hurricane

Slide 11

Slide 11

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world

Slide 12

Slide 12

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world

Slide 13

Slide 13

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world

Slide 14

Slide 14

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> Here comes the story of the Hurricane The man the authorities came to blame For somethin’ that he never done Put in a prison cell, but one time he could-a been The champion of the world </p> </main> </body> Hurricane

Slide 15

Slide 15

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <div> Here comes the story of the Hurricane </div> <div> The man the authorities came to blame </div> <div> For somethin’ that he never done </div> <div> Put in a prison cell, but one time he could-a been </div> <div> The champion of the world </div> </p> </main> </body> Hurricane ✘

Slide 16

Slide 16

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <span> Here comes the story of the Hurricane </span> <span> The man the authorities came to blame </span> <span> For somethin’ that he never done </span> <span> Put in a prison cell, but one time he could-a been </span> <span> The champion of the world </span> </p> </main> </body> Hurricane

Slide 17

Slide 17

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <span> Here comes the story of the Hurricane </span> <span> The man the authorities came to blame </span> <span> For somethin’ that he never done </span> span <span> Put in a prison cell, but one time he could-a been </span> { <span> The champion of the world </span> display: block; </p> } </main> </body> Hurricane ✘

Slide 18

Slide 18

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> class=”line”> Here comes the story of the Hurricane </span> class=”line”> The man the authorities came to blame </span> class=”line”> For somethin’ that he never done </span> class=”line”> Put in a prison cell, but one time he could-a been </span> class=”line”> The champion of the world </span> </p> </main> </body> Hurricane <span <span <span <span <span

Slide 19

Slide 19

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> class=”line”> Here comes the story of the Hurricane </span> class=”line”> The man the authorities came to blame </span> class=”line”> For somethin’ that he never done </span> .line class=”line”> Put in a prison cell, but one time he could-a been </span> { class=”line”> The champion of the world </span> display: block; </p> } </main> </body> Hurricane <span <span <span <span <span

Slide 20

Slide 20

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane / The man the authorities came to blame / For somethin’ that he never done / Put in a prison cell, but one time he could-a been / The champion of the world

Slide 21

Slide 21

HURRICANE by Bob Dylan and Jacques Levy Here comes the story of the Hurricane / The man the authorities came to blame / For somethin’ that he never done / Put in a prison cell, but one time he could-a been / The champion of the world .line:not(:last-child)::after { content: ” / “; }

Slide 22

Slide 22

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> class=”line”> Here comes the story of the Hurricane </span> class=”line”> The man the authorities came to blame </span> class=”line”> For somethin’ that he never done </span> class=”line”> Put in a prison cell, but one time he could-a been </span> class=”line”> The champion of the world </span> </p> </main> </body> Hurricane <span <span <span <span <span

Slide 23

Slide 23

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <l> Here comes the story of the Hurricane </l> <l> The man the authorities came to blame </l> <l> For somethin’ that he never done </l> <l> Put in a prison cell, but one time he could-a been </l> <l> The champion of the world </l> </p> </main> </body> Hurricane

Slide 24

Slide 24

Custom Elements

Slide 25

Slide 25

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <x-l> Here comes the story of the Hurricane </x-l> <x-l> The man the authorities came to blame </x-l> <x-l> For somethin’ that he never done </x-l> <x-l> Put in a prison cell, but one time he could-a been </x-l> <x-l> The champion of the world </x-l> </p> </main> </body> Hurricane

Slide 26

Slide 26

<body> <main> <h1> </h1> <footer> by Bob Dylan and Jacques Levy </footer> <p> <x-l> Here comes the story of the Hurricane </x-l> <x-l> The man the authorities came to blame </x-l> <x-l> For somethin’ that he never done </x-l> x-l <x-l> Put in a prison cell, but one time he could-a been </x-l> { <x-l> The champion of the world </x-l> display: block; </p> padding-left: 1em; </main> text-indent: -1em; </body> } Hurricane

Slide 27

Slide 27

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 28

Slide 28

Three bodies lyin’ there does Patty see And another man named Bello, movin’ around mysteriously “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 29

Slide 29

Three bodies lyin’ there does Patty see And another man named Bello, movin’ around mysteriously “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 30

Slide 30

Three bodies lyin’ there does Patty see And another man named Bello, movin’ around mysteriously “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 31

Slide 31

Three bodies lyin’ there does Patty see And another man named Bello, movin’ around mysteriously “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 32

Slide 32

Three bodies lyin’ there does Patty see And another man named Bello, movin’ around mysteriously “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 33

Slide 33

x-l { display: block; padding-left: 1em; text-indent: -1em; Three bodies lyin’ there does Patty see hanging-punctuation: first allow-end last; And another man named Bello, movin’ around mysteriously } “I didn’t do it,” he says, and he throws up his hands “I was only robbin’ the register, I hope you understand I saw them leavin’,” he says, and he stops “One of us had better call up the cops” And so Patty calls the cops…

Slide 34

Slide 34

hanging-punctuation ✘ ✘ ✘ ✘ ✘

Slide 35

Slide 35

for (var xlElements = document.querySelectorAll(‘x-l’), l = xlElements.length, i = 0; i < l; i++) { if (/^([„“]+)/.test(xlElements[i].innerHTML)) { xlElements[i].classList.add(‘hanging-punctuation-start’); } }

Slide 36

Slide 36

.hanging-punctuation-start for { (var xlElements = document.querySelectorAll(‘x-l’), l = xlElements.length, text-indent: -1.42em;i = 0; i < l; i++) { } if (/^([„“]+)/.test(xlElements[i].innerHTML)) { xlElements[i].classList.add(‘hanging-punctuation-start’); } } ✘

Slide 37

Slide 37

for (var xlElements = document.querySelectorAll(‘x-l’), l = xlElements.length, i = 0; i < l; i++) { xlElements[i].innerHTML = xlElements[i].innerHTML .replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’); }

Slide 38

Slide 38

x-l { display: block; padding-left: 1em; -1em; for (var xlElements = text-indent: document.querySelectorAll(‘x-l’), position: l = xlElements.length, i = 0; relative; i < l; i++) } { xlElements[i].innerHTML = xlElements[i].innerHTML .hanging-punctuation-start .replace(/^([„“’]+)/, { ‘<span class=”hanging-punctuation-start”>$1</span>’); position: absolute; right: 100%; text-align: right; }

Slide 39

Slide 39

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 40

Slide 40

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { this.innerHTML = this.innerHTML.replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’); } var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 41

Slide 41

Shadow DOM

Slide 42

Slide 42

<input placeholder=”…”> <input type=”range”> <audio>

Slide 43

Slide 43

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { this.innerHTML = this.innerHTML.replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’); } var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 44

Slide 44

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’); var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 45

Slide 45

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, .hanging-punctuation-start ‘<span class=”hanging-punctuation-start”>$1</span>’); { } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 46

Slide 46

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, x-l::shadow .hanging-punctuation-start ‘<span class=”hanging-punctuation-start”>$1</span>’); { } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 47

Slide 47

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, x-l::shadow .hanging-punctuation-start It’s been suggested to ‘<span class=”hanging-punctuation-start”>$1</span>’); { remove this feature. } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 48

Slide 48

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, x-l /deep/ .hanging-punctuation-start ‘<span class=”hanging-punctuation-start”>$1</span>’); { } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 49

Slide 49

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, x-l >>> .hanging-punctuation-start ‘<span class=”hanging-punctuation-start”>$1</span>’); { } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 50

Slide 50

x-l { var XLElementPrototype =display: Object.create(HTMLSpanElement.prototype); block; padding-left: 1em; XLElementPrototype.createdCallback function() text-indent:= -1em; { position: relative; var root = this.createShadowRoot(); } It’s currently disputed root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, x-l >>> .hanging-punctuation-start whether this combinator ‘<span class=”hanging-punctuation-start”>$1</span>’); { should exist. } position: absolute; right: 100%; var XLElement = document.registerElement(‘x-l’, { text-align: right; prototype: XLElementPrototype } });

Slide 51

Slide 51

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’); var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 52

Slide 52

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { var root = this.createShadowRoot(); } root.innerHTML = this.innerHTML.replace(/^([„“’]+)/, ‘<span class=”hanging-punctuation-start”>$1</span>’) + ‘<style>.hanging-punctuation-start { position: absolute;\ right: 100%; text-align: right }</style>’; var XLElement = document.registerElement(‘x-l’, {

Slide 53

Slide 53

Templates

Slide 54

Slide 54

<template id=”hanging-punctuation-start-template”> <span class=”hanging-punctuation-start”></span> <style> .hanging-punctuation-start { position: absolute; right: 100%; text-align: right; } </style> </template>

Slide 55

Slide 55

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { var result = /^([„“’]+)/.exec(this.innerHTML); if (result) { var template = document.querySelector(‘#hanging-punctuation-start-template’); template.content.querySelector(‘.hanging-punctuation-start’).innerHTML = result[0]; var clone = document.importNode(template.content, true); var root = this.createShadowRoot(); root.innerHTML = this.innerHTML.substr(result[0].length); root.appendChild(clone); } } var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 56

Slide 56

HTML Imports

Slide 57

Slide 57

hanging-punctuation-start-template.html <template id=”hanging-punctuation-start-template”> <span class=”hanging-punctuation-start”></span> <style> .hanging-punctuation-start { position: absolute; right: 100%; text-align: right; } </style> </template>

<link rel=”import” href=”hanging-punctuation-start-template.html”>

Slide 58

Slide 58

var XLElementPrototype = Object.create(HTMLSpanElement.prototype); XLElementPrototype.createdCallback = function() { var result = /^([„“’]+)/.exec(this.innerHTML); if (result) { var link = document.querySelector(‘link[rel=”import”]’); var template = link.import.querySelector(‘#hanging-punctuation-start-template’); template.content.querySelector(‘.hanging-punctuation-start’).innerHTML = result[0]; var clone = document.importNode(template.content, true); var root = this.createShadowRoot(); root.innerHTML = this.innerHTML.substr(result[0].length); root.appendChild(clone); } } var XLElement = document.registerElement(‘x-l’, { prototype: XLElementPrototype });

Slide 59

Slide 59

Templates HTML Imports Custom Elements Shadow DOM ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✔ ✘ ✘ ✘ ✔ ✘ ✘ ✘ ✔ ✘ ✘ ✘

Slide 60

Slide 60

progressive enhancement

Slide 61

Slide 61

An escalator can never break, it can only become stairs. You would never see an “Escalator Temporarily Out Of Order” sign, just “Escalator Temporarily Stairs. Sorry for the convenience. We apologize for the fact that you can still get up there.” —Mitch Hedberg

Slide 62

Slide 62

There’s a common misconception that progressive enhancement means that you’ll spend your time dealing with older browsers, but in fact the opposite is true. Putting the basic functionality into place doesn’t take very long at all. And once you’ve done that, you’re free to spend all your time experimenting with the latest and greatest browser technologies, secure in the knowledge that even if they aren’t universally supported yet, that’s okay: you’ve already got your fallback in place. —Jeremy Keith

Slide 63

Slide 63

The key to thinking about web development this way is realising that there isn’t one final interface— there could be many, slightly different interfaces depending on the properties and capabilities of any particular user agent at any particular moment. And that’s okay. Websites do not need to look the same in every browser. —Jeremy Keith

Slide 64

Slide 64

Once you truly accept that, it’s an immensely liberating idea. Instead of spending your time trying to make websites look the same in wildly varying browsers, you can spend your time making sure that the core functionality of what you’re building works everywhere, while providing the best possible experience for more capable browsers. —Jeremy Keith

Slide 65

Slide 65

Slide 66

Slide 66

<table> <caption>Bruce Springsteen’s studio albums</caption> <thead> <tr> <th>title</th> <th>release date</th> </tr> </thead> <tbody> <tr> <td>Greetings from Asbury Park, N.J.</td> <td> <time>1973-01-05</time> </td> </tr>

Slide 67

Slide 67

<table sortable> <caption>Bruce Springsteen’s studio albums</caption> <thead> <tr> <th>title</th> <th sorted>release date</th> </tr> </thead> <tbody> <tr> <td>Greetings from Asbury Park, N.J.</td> <td> <time>1973-01-05</time> </td> </tr> ▼

Slide 68

Slide 68

<table sortable> ✘ ✘ ✘ ✘ ✘

Slide 69

Slide 69

✘ <x-sortable-table> <caption>Bruce Springsteen’s studio albums</caption> <thead> <tr> <th>title</th> <th sorted>release date</th> </tr> </thead> <tbody> <tr> <td>Greetings from Asbury Park, N.J.</td> <td> <time>1973-01-05</time> </td> </tr> ▼

Slide 70

Slide 70

<table is=”x-sortable-table”> <caption>Bruce Springsteen’s studio albums</caption> <thead> <tr> <th>title</th> <th sorted>release date</th> </tr> </thead> <tbody> <tr> <td>Greetings from Asbury Park, N.J.</td> <td> <time>1973-01-05</time> </td> </tr> ▼

Slide 71

Slide 71

var XSortableTableElementPrototype = Object.create(HTMLTableElement.prototype); var XSortableTableElement = document.registerElement(‘x-sortable-table’, { prototype: XSortableTableElementPrototype, extends: ‘table’ }); → http://codepen.io/gunnarbittersmann/pen/XXwZpP

Slide 72

Slide 72

Custom Elements Shadow DOM Templates HTML Imports

Slide 73

Slide 73

Custom Elements Templates Look Ma, no JS framework! Shadow DOM HTML Imports

Slide 74

Slide 74

AngularJS

Slide 75

Slide 75

<!DOCTYPE html> <html ng-app=”analogClockApp”> <head> <meta charset=”UTF-8”/> <link rel=”stylesheet” href=”analog-clock.css”/> </head> <body ng-controller=”AnalogClockCtrl”> <time analog-clock/> <script src=”angular.min.js”></script> <script src=”analogClockApp.js”></script> </body> </html>

Slide 76

Slide 76

analogClockTemplate.html <div class=”face”> <div class=”hour-hand” style=”transform: rotate({{ 30 * (now | date:’h’) + (now | date:’m’) / 2 }}deg)”></div> <div class=”minute-hand” style=”transform: rotate({{ 6 * (now | date:’m’) }}deg)”></div> <div class=”second-hand” style=”transform: rotate({{ 6 * (now | date:’s’) + (now | date:’m’) * 360 + (now | date:’h’) * 360 }}deg)”></div> </div>

Slide 77

Slide 77

.face { width: 250px; height: 250px; position: relative; background-position: left top; transform: translate(461px,51px) rotateX(46deg) rotateY(27deg) rotateZ(-38deg); } .hour-hand { width: 4%; height: 35%; background: hsl(0, 0%, 95%); position: absolute; left: 48%; top: 25%; transform-origin: center 71.43%; }

Slide 78

Slide 78

var analogClockApp = angular.module(‘analogClockApp’, []); analogClockApp.directive(‘analogClock’, function () { return { templateUrl: ‘analogClockTemplate.html’ }; });

Slide 79

Slide 79

analogClockApp.controller(‘AnalogClockCtrl’, function ($scope, $timeout) { $scope.now = ‘Loading…’; var updateTime = function () { $timeout(function () { $scope.now = new Date(); updateTime(); }, 1000); }; updateTime(); });

Slide 80

Slide 80

Slide 81

Slide 81

Web Components AngularJS

Slide 82

Slide 82

Web Components Hurricane Blinded by the Light written by Bob Dylan and Jacques Levy written by Bruce Springsteen Copenhagen Metro Escalators photo by Stig Nygaard, CC-BY-2.0