Slide 1

Slide 1 text

Web Component Design Designing Frontends for Reusability TEGERNSEE, 23 FEB 2019
 JOY HERON

Slide 2

Slide 2 text

2 Web Component Design Joy Heron Consultant @ INNOQ [email protected] @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 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 Joy Heron / @iamjoyheron

Slide 19

Slide 19 text

Common Excuses for not using screen readers 19 MORE Web Component Design Joy Heron / @iamjoyheron Apple VoiceOver ChromeVox Orca Excuse #1: I don’t have one! Free Screen Readers:

Slide 20

Slide 20 text

Common Excuses for not using screen readers 20 Web Component Design Joy Heron / @iamjoyheron Excuse #2: It’s HARD! Yes. But if you have time to focus on accessibility, then look into it!

Slide 21

Slide 21 text

21 Web Component Design DON’T design your components based on how YOU use a screen reader BUT the basic commands are pretty easy to figure out! Read up on Accessibility!!!

Slide 22

Slide 22 text

22 Web Component Design let’s try it out… Joy Heron / @iamjoyheron

Slide 23

Slide 23 text

23 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 24

Slide 24 text

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

Slide 25

Slide 25 text

25 make it pretty?? Web Component Design

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

28 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 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

32 make it work Web Component Design

Slide 33

Slide 33 text

33 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 34

Slide 34 text

34 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 35

Slide 35 text

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

Slide 36

Slide 36 text

36 make it pretty Web Component Design

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

38 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 39

Slide 39 text

39 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 40

Slide 40 text

40 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 41

Slide 41 text

41 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 42

Slide 42 text

42 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 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

45 Web Component Design before we move on to the next component… Joy Heron / @iamjoyheron

Slide 46

Slide 46 text

46 Web Component Design Tip #4 Use a templating engine as an abstraction for your component Joy Heron / @iamjoyheron

Slide 47

Slide 47 text

47 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 48

Slide 48 text

input and select components 48 Web Component Design Joy Heron / @iamjoyheron {{!— input.hbs }} {{!— select.hbs }} {{> @partial-block}} {{> input name=“col1” label=“Column 1” }} {{#> select name=“col2” label=“Column 2” }} A B … Z {{/select}}

Slide 49

Slide 49 text

arrows component 49 Web Component Design Joy Heron / @iamjoyheron {{!— arrows.hbs }} Sort {{label}} Ascending Sort {{label}} Descending {{> arrows name=“col3” label=“Column 3” }}

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

51 make it work Web Component Design

Slide 52

Slide 52 text

52 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1: make it work
Column 1 {{> arrows name=“col1” label=“Column 1” }} {{> input name=“col1” label=“Column 1” }}

Slide 53

Slide 53 text

53 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 54

Slide 54 text

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

Slide 55

Slide 55 text

55 Web Component Design Table Header Component Joy Heron / @iamjoyheron Step 1.2: make it accessible
Column 1 {{> arrows name=“col1” label=“Column 1” }} {{> input name=“col1” label=“Column 1” }}
Column 1 {{> arrows name=“col1” label=“Column 1” }} {{> input name=“col1” label=“Column 1” }}

Slide 56

Slide 56 text

56 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 57

Slide 57 text

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

Slide 58

Slide 58 text

58 make it pretty Web Component Design

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

60 Web Component Design Tip #6 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 61

Slide 61 text

61 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 62

Slide 62 text

62 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 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

65 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 66

Slide 66 text

66 Web Component Design Table Header Component Joy Heron / @iamjoyheron Our HTML Template for reuse ❤ {{!— tabelle-header.hbs }}
{{label}} {{> arrows }} {{#if select}} {{> select }} {{else}} {{> input }} {{/if}}

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

70 Web Component Design make it work

Slide 71

Slide 71 text

71 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 1: make it work {{> tabelle-header name=“col1” label=“Column 1” }} {{> tabelle-header name=“col2” label=“Column 2” }} {{> tabelle-header name=“col3” label=“Column 3” }} … Perform Search Wrap in a form so that we can submit user filter queries

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

75 make it better Web Component Design

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

77 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 78

Slide 78 text

78 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 79

Slide 79 text

79 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 80

Slide 80 text

80 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 81

Slide 81 text

81 Web Component Design Tip #9 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 82

Slide 82 text

82 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 83

Slide 83 text

83 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 84

Slide 84 text

84 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 85

Slide 85 text

85 Web Component Design Tabelle Joy Heron / @iamjoyheron Step 3: Further possible improvements Replace whole instead of for added flexibility Integrate with the History API so the user can go back and forth in their search history Instantiate the filter fields on the client

Slide 86

Slide 86 text

86 Web Component Design Joy Heron / @iamjoyheron

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

88 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/