Slide 1

Slide 1 text

Web Component Design Maintaining and Reusing your Frontend MÜNCHEN .NET MEETUP, 12. FEBRUARY 2020
 JOY HERON

Slide 2

Slide 2 text

2 Web Component Design Joy Heron Senior Consultant @ INNOQ joy.heron@innoq.com @iamjoyheron

Slide 3

Slide 3 text

3 Web Component Design Photo by Brett Patzke on Unsplash My Journey

Slide 4

Slide 4 text

4 Spring 2016 Web Component Design

Slide 5

Slide 5 text

5 Web Component Design Spring 2016 Summer 2016 Spring 2017 Winter 2017 Fall 2018 … and in the future??

Slide 6

Slide 6 text

6 Web Component Design Why am I always implementing the same thing?

Slide 7

Slide 7 text

7 Web Component Design Would I do this in the backend? Joy Heron / @iamjoyheron

Slide 8

Slide 8 text

8 Web Component Design Abstraction - Rule of Three Joy Heron / @iamjoyheron Step 1: copy Step 2: copy Step 3: Abstract!

Slide 9

Slide 9 text

9 Time to find an abstraction! Web Component Design GET /search?col1=x&col2=y 200 OK …

Slide 10

Slide 10 text

And now let’s build it with web components! 10 Web Component Design That’s what I did. It’s called: Tabelle https://github.com/innoq/tabelle

Slide 11

Slide 11 text

11 Web Component Design What is a web component? Joy Heron / @iamjoyheron HTML CSS probably JavaScript possibly make it pretty make it better make it work FOR ALL USERS!

Slide 12

Slide 12 text

12 Web Component Design Single Responsibility Principle Joy Heron / @iamjoyheron Each Component should do ONE thing REALLY WELL If you need more functionality, write a new component and compose them! Benefit of HTML First: HTML composes naturally!

Slide 13

Slide 13 text

13 Web Component Design How to find web components? Joy Heron / @iamjoyheron Atomic Design: http://atomicdesign.bradfrost.com/

Slide 14

Slide 14 text

14 Web Component Design Joy Heron / @iamjoyheron F I R S T C O M P O N E N T

Slide 15

Slide 15 text

15 make it work Web Component Design

Slide 16

Slide 16 text

16 Web Component Design Filter Field Component Joy Heron / @iamjoyheron Step 1: make it work GET /search? col1=my+search+str& col2=Option+1 What we need Option 1 Option 2 Option 3 Search How to get it

Slide 17

Slide 17 text

17 it works! Web Component Design FOR ALL USERS??

Slide 18

Slide 18 text

18 Web Component Design Tip #1 If you want to make sure you are accessible ACTUALLY USE A SCREEN READER Read this book for more tips!

Slide 19

Slide 19 text

19 Web Component Design Joy Heron / @iamjoyheron Currently Edit Text.
 You are currently on a text field inside of web content… Solution Filter Field Component Step 1.2: make it accessible Filter Column 1, Edit Text.
 You are currently on a text field inside of web content… ❤ Let’s test our component with a screen reader to see how it sounds!

Slide 20

Slide 20 text

20 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS!

Slide 21

Slide 21 text

21 make it pretty?? Web Component Design

Slide 22

Slide 22 text

22 CSS is harder to OVERRIDE than it is to WRITE Web Component Design Joy Heron / @iamjoyheron

Slide 23

Slide 23 text

23 Web Component Design Tip #2 Use minimal CSS to make it easy to override your styles later! Joy Heron / @iamjoyheron

Slide 24

Slide 24 text

24 Web Component Design Joy Heron / @iamjoyheron … Add a CSS class to our component to allow users to write their own styles later! Filter Field Component Step 2: make it pretty

Slide 25

Slide 25 text

25 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS someone else can make it pretty! JS? make it better? ?

Slide 26

Slide 26 text

26 Web Component Design Joy Heron / @iamjoyheron CSS someone else can make it pretty! JS no need! HTML it works FOR ALL USERS!

Slide 27

Slide 27 text

27 Web Component Design Joy Heron / @iamjoyheron S E C O N D C O M P O N E N T

Slide 28

Slide 28 text

28 make it work Web Component Design

Slide 29

Slide 29 text

29 Web Component Design Sort Button Component Joy Heron / @iamjoyheron Step 1: make it work GET /search?sort=col1_asc What we need Sort Column 1 Ascending Search How to get it HTML Element? Sort Column 2 Descending

Slide 30

Slide 30 text

30 Web Component Design Joy Heron / @iamjoyheron Sort Column 1 Ascending, radio button, 1 of 2
 You are currently on a radio button, 1 of 2, … ❤ Sort Button Component Step 1.2: make it accessible

Slide 31

Slide 31 text

31 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS!

Slide 32

Slide 32 text

32 make it pretty Web Component Design

Slide 33

Slide 33 text

33 Web Component Design Tip #3 You can select an input element by clicking on it’s HTML label Joy Heron / @iamjoyheron

Slide 34

Slide 34 text

34 Web Component Design Joy Heron / @iamjoyheron Visually hide radio buttons and style their labels. Sort Button Component Step 2: make it pretty %visually-hidden { position: absolute !important; height: 1px; width: 1px; overflow: hidden; clip: rect(1px 1px 1px 1px); /* IE6, IE7 */ clip: rect(1px, 1px, 1px, 1px); } .visually-hidden { @extend %visually-hidden; } Source: https://snook.ca/archives/html_and_css/hiding-content-for-accessibility Move content offscreen so it is not visible but can still be read by a screen reader. SCSS Placeholder Selector & Util CSS Class

Slide 35

Slide 35 text

35 Web Component Design Joy Heron / @iamjoyheron Sort Column 1 Ascending Sort Column 2 Descending Visually hide radio buttons and style their labels. .arrow { @extend %visually-hidden; } .arrow—asc { @include icon-before(‘up.svg’, #acacac); cursor: pointer; } .arrow—desc { @include icon-before(‘down.svg’, #acacac); cursor: pointer; } So far, so good: Sort Button Component Step 2: make it pretty

Slide 36

Slide 36 text

36 Web Component Design Joy Heron / @iamjoyheron • Style checked, focused, and hover input states! • Because our label follows our input, we can style this using pure CSS with a ‘+’! .arrow:checked + .arrow—asc::before, .arrow:checked + .arrow—desc::before { background-color: #535353; } .arrow:focus + .arrow—asc::before, .arrow:focus + .arrow—desc::before, .arrow:hover + .arrow—asc::before, .arrow:hover + .arrow—desc::before { background-color: #6882cb; } Result: focused: checked: Sort Button Component Step 2: make it pretty

Slide 37

Slide 37 text

37 Web Component Design Joy Heron / @iamjoyheron Hide our labels visually Sort Column 1 Ascending Sort Column 1 Descending base: checked: focused: Sort Button Component Step 2: make it pretty

Slide 38

Slide 38 text

38 Web Component Design Joy Heron / @iamjoyheron Sort Button Component Step 2.2: Double-check accessibility Sort Column 1 Ascending, radio button, 1 of 2
 You are currently on a radio button, 1 of 2, … ❤

Slide 39

Slide 39 text

39 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS it’s pretty! JS? make it better? ?

Slide 40

Slide 40 text

40 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS it’s pretty! JS we’re ok for now!

Slide 41

Slide 41 text

41 Web Component Design We’ve written some components… But how can we MAINTAIN them over time? Joy Heron / @iamjoyheron

Slide 42

Slide 42 text

42 Web Component Design Tip #4 Use a templating engine as an abstraction for your component 
 Possible templating engines: JSX / complate, Handlebars Joy Heron / @iamjoyheron

Slide 43

Slide 43 text

43 Web Component Design Benefits of HTML Templates for web components Joy Heron / @iamjoyheron Change ONCE, modify all instances Easier to MAINTAIN components over time PUBLISH templates as an npm library for use in other projects

Slide 44

Slide 44 text

Title Text 44 Web Component Design Photo by Jens Lelie on Unsplash We need a big mental shift: From “Let’s Build a Website” to “Let’s maintain a product which other products use as a dependency” - Brad Frost

Slide 45

Slide 45 text

input and select components 45 Web Component Design Joy Heron / @iamjoyheron /* input.jsx */ function Input ({ name, label }, …kids) { return } /* select.jsx */ function Select ({ name, label }, …kids) { return {kids} A B … Z

Slide 46

Slide 46 text

arrows component 46 Web Component Design Joy Heron / @iamjoyheron /* arrows.jsx */ function Input ({ name, label }, …kids) { return <> Sort {label} Ascending Sort {label} Descending > }

Slide 47

Slide 47 text

47 Web Component Design Joy Heron / @iamjoyheron Tip #5 Develop your components using a pattern library or living styleguide (patternlab.io, fractal) BONUS POINTS:
 publish your pattern library as reusable templates

Slide 48

Slide 48 text

48 Web Component Design How does this work in a team? Joy Heron / @iamjoyheron

Slide 49

Slide 49 text

49 Web Component Design Joy Heron / @iamjoyheron A simple team

Slide 50

Slide 50 text

50 Web Component Design Joy Heron / @iamjoyheron A new product with the same design system? But what happens when we have to split our teams?

Slide 51

Slide 51 text

Web components provide a language between backend and frontend 51 Web Component Design Cross-functional Team Joy Heron / @iamjoyheron Backend Database Platform Frontend Design & UX Design- Dev Fullstack-Dev Fullstack-Dev Frontend- Dev Backend-Dev Devops-Dev

Slide 52

Slide 52 text

52 Web Component Design Joy Heron / @iamjoyheron Collaboration with split teams AVOID throwing components over the wall! Namespace .star-… Namespace .heart-… Namespace .global-…

Slide 53

Slide 53 text

53 Web Component Design Joy Heron / @iamjoyheron There are loads of different team constellations but in all of them we want… to RELEASE new versions of our component library quickly as we develop new ideas to UPGRADE to new versions of our component library quickly WITHOUT worrying about breaking changes

Slide 54

Slide 54 text

54 Web Component Design Tip #6 When publishing a library of templates, keep the components backward compatible to make it easy to upgrade! Joy Heron / @iamjoyheron

Slide 55

Slide 55 text

55 Web Component Design Joy Heron / @iamjoyheron How to make a component backward compatible When adding new parameters, make them OPTIONAL or add a default value Instead of breaking a component, create a COPY of the existing implementation and give it a NEW NAME Avoid DEPENDING too strongly on the structure of the component when using it in other components or in UI Tests

Slide 56

Slide 56 text

56 Web Component Design …now to our next component… Joy Heron / @iamjoyheron

Slide 57

Slide 57 text

57 Web Component Design Joy Heron / @iamjoyheron T H I R D C O M P O N E N T

Slide 58

Slide 58 text

58 make it work Web Component Design

Slide 59

Slide 59 text

59 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1: make it work
Column 1

Slide 60

Slide 60 text

60 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1.2: make it accessible Column 1 → Sort Column 1 Ascending, radio button 1 of 6 Sort Column 1 Ascending → → Sort Column 1 Descending, radio button 2 of 6 Sort Column 1 Descending → Filter Column 1, edit text → → Column 2 → Sort Column 2 Ascending, radio button 3 of 6 Sort Column 2 Ascending → → Sort Column 2 Descending, radio button 4 of 6 → …

Slide 61

Slide 61 text

61 Web Component Design Tip #7 Wrap input fields in a role=group to add a navigation level for a screen reader Joy Heron / @iamjoyheron Note: The built-in HTML Element fieldset provides similar behavior but is extremely difficult to style

Slide 62

Slide 62 text

62 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1.2: make it accessible
Column 1
Column 1

Slide 63

Slide 63 text

63 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1.2: make it accessible Column 1, group VO+→ Column 2, group VO + ↓ Column 2 → Sort Column 2 Ascending, radio button 3 of 6 Sort Column 2 Ascending → → Sort Column 2 Descending, radio button 4 of 6 Sort Column 2 Descending → Filter Column 2, edit text → Navigation via groups with a screen reader! ❤

Slide 64

Slide 64 text

64 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS!

Slide 65

Slide 65 text

65 make it pretty Web Component Design

Slide 66

Slide 66 text

R.I.P Whack-A-Mole CSS 66 Web Component Design Joy Heron / @iamjoyheron

Slide 67

Slide 67 text

67 Web Component Design Tip #8 Design CSS like a box of chocolates Design your container with love and place your components inside it. (use Flexbox or CSS Grid) Joy Heron / @iamjoyheron

Slide 68

Slide 68 text

68 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 2: make it pretty Define a CSS Grid for Layout HEADER ▲ ▼ SEARCH .tabelle-header { display: grid; grid-template-areas: “header arrow-asc” “header arrow-desc” “search search”; } 1fr Takes up a proportional amount of grid auto grid-template-columns: 1fr auto;

Slide 69

Slide 69 text

69 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 2: make it pretty Place children items in grid HEADER ▲ ▼ SEARCH .tabelle-header { .header { grid-area: header; } .arrow—asc { grid-area: arrow-asc; } .arrow—desc { grid-area: arrow-desc; } .tabelle-input { grid-area: search; } } .tabelle-header { grid-template-areas: “header arrow-asc” “header arrow-desc” “search search”; }

Slide 70

Slide 70 text

70 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 2: make it pretty Add new styling classes Column 1 … Almost there!!!

Slide 71

Slide 71 text

71 Web Component Design Tip #9 With CSS Grid or Flexbox, vertical alignment is finally easy! Joy Heron / @iamjoyheron

Slide 72

Slide 72 text

72 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 2: make it pretty Center table header vertically .tabelle-header { .header { align-self: center; } } ❤

Slide 73

Slide 73 text

73 Web Component Design Table Header Component Joy Heron / @iamjoyheron Our HTML Template for reuse ❤ /* tabelle-header.jsx */ function TabelleHeader ({ name, label }, …kids) { return
{label} {kids ? {kids} : }
}

Slide 74

Slide 74 text

74 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS it’s pretty! JS? make it better? ?

Slide 75

Slide 75 text

75 Web Component Design Joy Heron / @iamjoyheron Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS it’s pretty! JS no need!

Slide 76

Slide 76 text

76 Web Component Design Joy Heron / @iamjoyheron L A S T C O M P O N E N T

Slide 77

Slide 77 text

77 Web Component Design make it work

Slide 78

Slide 78 text

78 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 1: make it work … Perform Search Wrap in a form so that we can submit user filter queries

Slide 79

Slide 79 text

79 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 1: make it work ❤ The magic of FORMS

Slide 80

Slide 80 text

80 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS? make it pretty? ?

Slide 81

Slide 81 text

81 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS someone else can make it pretty!

Slide 82

Slide 82 text

82 make it better Web Component Design

Slide 83

Slide 83 text

83 Web Component Design Tip #10 Submit forms asynchronously and replace your DOM with the result from the server Joy Heron / @iamjoyheron

Slide 84

Slide 84 text

84 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Submitting forms asynchronously // stringify form data as `application/x-www-form-urlencoded` function serializeForm (form) { … } function submit (form) { const uri = serializeForm(form) return fetch(uri) .then(response => response.text()) } ⚠ Only the application happy path is being considered here. Source: https://github.com/FND/uitil

Slide 85

Slide 85 text

85 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Replacing the tbody with response HTML function template2dom (htmlString, selector) { let tmp = document.createElement(‘template’) tmp.innerHTML = htmlString.trim() return tmp.content.querySelector(selector) } function replaceTbody (tbody, htmlResponse) { let newTbody = template2dom(htmlResponse, ‘tbody’) tbody.innerHTML = newTBody.innerHTML } ⚠ Only the application happy path is being considered here.

Slide 86

Slide 86 text

86 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Override default submit behavior function doSubmit (component, form) { let tbody = component.querySelector(‘tbody’) submit(form) .then(html => replaceTBody(tbody, html)) } function initializeSubmit (component, form) { form.addEventListener(‘submit’, ev => { doSubmit(component, form) ev.preventDefault() }) } ⚠ Only the application happy path is being considered here. Who calls the initialize function?

Slide 87

Slide 87 text

87 Web Component Design Historically: adding a component dynamically used to require BOTH the HTML Markup and a function to initialize it Joy Heron / @iamjoyheron

Slide 88

Slide 88 text

88 Web Component Design Tip #11 Use Custom Elements to define a custom HTML Element AND define how to initialize it. The browser will then initialize your component for you! Joy Heron / @iamjoyheron

Slide 89

Slide 89 text

89 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Custom Elements allow you to define how the component needs to be initialized. class Tabelle extends HTMLElement { connectedCallback () { let form = this.form initializeSubmit(this, form) } get form () { return this.querySelector(‘form’) } } customElements.define('ta-belle', Tabelle) … As soon as ta-belle appears in the DOM, connectedCallback will be called! ✨

Slide 90

Slide 90 text

90 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Custom Elements provide scope: this is bound to the HTML Element itself and it’s native JavaScript API class Tabelle extends HTMLElement { connectedCallback () { let form = this.form initializeSubmit(this, form) } get form () { return this.querySelector(‘form’) } } customElements.define('ta-belle', Tabelle)

Slide 91

Slide 91 text

91 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: make it better Make the implementation more dynamic by triggering submits when an input element changes import { debounce } from ‘util’ class Tabelle extends HTMLElement { connectedCallback () { … form.addEventListener(‘change’, () => doSubmit(this, form)) form.addEventListener(‘keyup’, debounce(300, ev => doSubmit(this, form))) } } Make sure to debounce ‘keyups’ for input[type=text] elements! https://davidwalsh.name/javascript-debounce-function ⚠ Only the application happy path is being considered here.

Slide 92

Slide 92 text

92 …and much more… Web Component Design

Slide 93

Slide 93 text

93 Web Component Design Joy Heron / @iamjoyheron

Slide 94

Slide 94 text

94 Web Component Design Joy Heron / @iamjoyheron HTML it works FOR ALL USERS! CSS it’s pretty! JS faster & dynamic

Slide 95

Slide 95 text

95 Web Component Design Joy Heron / @iamjoyheron https://github.com/innoq/tabelle https://github.com/innoq/tabelle_rails_example Thank you! https://tabelle-rails-example.herokuapp.com/