Web components through the eyes of a newcomer

A presentation at Design 4 Drupal Webinar Series in December 2020 in by Brian Perry

Slide 1

Slide 1

Web components through the eyes of a newcomer Design 4 Drupal - December 2, 2020 https://bit.ly/d4dwc

Slide 2

Slide 2

Brian Perry » Front End Architect at Bounteous » Rocking the Chicago ‘burbs » Lover of all things components… and Nintendo @bricomedy

Slide 3

Slide 3

Slide 4

Slide 4

Components have taken over the web

Slide 5

Slide 5

We’re here to talk about Web Components

Slide 6

Slide 6

Like React and Angular, right?

Slide 7

Slide 7

Web components A set of web platform APIs, not tied to a specific framework » Custom elements » Shadow DOM » HTML Templates

Slide 8

Slide 8

I ❤ components. So I should ❤ Web Components. ! So why aren’t they part of my workflow?

Slide 9

Slide 9

Warning: not an expert

Slide 10

Slide 10

I look to: @btopro @salem_cobalt @castastrophee @illepic

Slide 11

Slide 11

Can I Use Web Components? 1 IE 11 can be supported using polyfills 1

Slide 12

Slide 12

Can Can I Use Use Web Components? 2 Find many more examples at https://wild.open-wc.org/ 2

Slide 13

Slide 13

I should build an example web component. But what could be a relevant example in November 2020?

Slide 14

Slide 14

Slide 15

Slide 15

Slide 16

Slide 16

Slide 17

Slide 17

Slide 18

Slide 18

Slide 19

Slide 19

Slide 20

Slide 20

Slide 21

Slide 21

Slide 22

Slide 22

My own personal election tracker

Slide 23

Slide 23

Using <election-tracker> » Import script as JS module. Could be: » local file » NPM dependency (@backlineint/results-tracker) » Use your custom element in markup » Pass data in using attributes Note: If your component has external dependencies, you’ll need to use a bundler (Webpack, Rollup, Parcel, etc.)

Slide 24

Slide 24

Custom Elements 3 3 https://codepen.io/brianperry/pen/RwGPLBx

Slide 25

Slide 25

Shadow DOM » Encapsulated DOM Tree » Separate from main DOM » Elements won’t collide » Scoped styles » Super spooky

Slide 26

Slide 26

Scoped Styles 4 4 https://codesandbox.io/s/election-results-tracker-wdxvx?file=/src/styles.css

Slide 27

Slide 27

Global Styling TBH, I’m still confused ! » CSS Inheritance » CSS custom properties (css variables) » CSS ::part » <div part=”headline”> » results-tracker::part(headline) { color: red; } » Opt out of the shadow DOM

Slide 28

Slide 28

Building <election-tracker> Take 1: Vanilla JS

Slide 29

Slide 29

Rendering a Headline index.html <html> <head> <title>Results Tracker Heading</title> <meta charset=”UTF-8” /> </head> <body> <results-tracker headline=”Race Between Old Men Too Close To Call” /> <script type=”module” src=”src/results-tracker.js”></script> </body> </html>

Slide 30

Slide 30

results-tracker.js class ResultsTracker extends HTMLElement { constructor() { // Always call super first in constructor super(); // Create a shadow root const shadow = this.attachShadow({ mode: “open” }); // sets and returns ‘this.shadowRoot’ // Create wrapping element const wrapper = document.createElement(“div”); wrapper.setAttribute(“class”, “results-tracker”); this.headlineElement = document.createElement(“h2”); this.headlineElement.setAttribute(“class”, “results-tracker__headline”); this.headlineElement.textContent = this.getAttribute(“headline”); wrapper.appendChild(this.headlineElement); // Attach the results tracker to the shadow DOM. shadow.appendChild(wrapper); } } // Define the new element customElements.define(“results-tracker”, ResultsTracker);

Slide 31

Slide 31

Refactoring to use <template> class ResultsTracker extends HTMLElement { constructor() { super(); this.shadow = this.attachShadow({ mode: “open” }); // Templates are not referenced in the DOM, but can be referenced / cloned using js const template = document.createElement(“template”); template.innerHTML = <div class="results-tracker"> <div class="results-tracker__headline"> <h2>${this.getAttribute("headline")}</h2> </div> </div>; // Attach the template to the Shadow DOM this.shadow.appendChild(template.content); } } customElements.define(“results-tracker”, ResultsTracker);

Slide 32

Slide 32

Add scoped styling class ResultsTracker extends HTMLElement { constructor() { super(); /* Removed for brevity… / // Create CSS to apply to the shadow dom const style = document.createElement(“style”); style.textContent = :host { font-family: 'Libre Franklin', helvetica, arial, sans-serif; } h2 { margin: .5rem 0; font-family: 'Domine', serif; font-weight: 700; font-size: 36px; text-align: center; }; // Attach the styles to the shadow dom shadow.appendChild(style); / Removed for brevity… */ } }

Slide 33

Slide 33

Observe attributes and re-render if changed class ResultsTracker extends HTMLElement { // Specify observed attributes for attributeChangedCallback static get observedAttributes() { return [“headline”]; } constructor() { /* Removed for brevity */ } // Custom element lifecycle callback function attributeChangedCallback(name, oldValue, newValue) { // Compare old to new to prevent unnecessary re-rendering if (oldValue !== newValue && name === “headline”) { this.shadow.querySelector( “.results-tracker__headline h2” ).textContent = newValue; } } } // Define the new element customElements.define(“results-tracker”, ResultsTracker);

Slide 34

Slide 34

5 That was too much work for a headline… Shouldn’t this be easier? 5 https://codesandbox.io/s/results-tracker-heading-vanilla-js-lunes

Slide 35

Slide 35

Wait for it…

Slide 36

Slide 36

So. Many. Libraries.

Slide 37

Slide 37

» LitElement » Stencil » FastElement » Haunted » Hybrids » Many more… Aren’t we just back where we started?

Slide 38

Slide 38

Only one way to find out… Building <election-tracker> Take 2: Lit-Element

Slide 39

Slide 39

Rendering a headline (LitElement version) index.html <html> <head> <title>Results Tracker Heading - Lit</title> <meta charset=”UTF-8” /> </head> <body> <results-tracker headline=”Race Between Old Men Too Close To Call” /> <script type=”module” src=”src/results-tracker.js”></script> </body> </html> (same as vanilla js version)

Slide 40

Slide 40

results-tracker.js import { LitElement, html } from “lit-element”; export class ResultsTracker extends LitElement { static get properties() { return { headline: { type: String } }; } render() { return html<div class="results-tracker"> <div class="results-tracker__headline"> <h2>${this.headline}</h2> </div> </div>; } } window.customElements.define(“results-tracker”, ResultsTracker);

Slide 41

Slide 41

A lot with less » Renders custom element » Templating » Observes updates to attributes Just need to add scoped styles.

Slide 42

Slide 42

Add scoped styling import { LitElement, html, css } from “lit-element”; export class ResultsTracker extends LitElement { static get styles() { return css:host { font-family: "Libre Franklin", helvetica, arial, sans-serif; } .results-tracker__headline h2 { margin: 0.5rem 0; font-family: "Domine", serif; font-weight: 700; font-size: 36px; text-align: center; }; } // Properties… // Render method… } window.customElements.define(“results-tracker”, ResultsTracker);

Slide 43

Slide 43

Converting attributes // Vanilla JS - have to manually transform string attributes processCandidates() { this.candidates = JSON.parse(this.getAttribute(‘candidates’)); // … } // Define the type of your property, and LitElement will automatically // handle conversion for you. static get properties() { return { /** * An array of objects containing data for each candidate */ candidates: {type: Array}, }; } (And many other DX niceties)

Slide 44

Slide 44

Feels like a more appropriate amount of work for a headline… and especially the full results-tracker 6 https://codesandbox.io/s/election-results-tracker-thk26 6

Slide 45

Slide 45

Stencil “Compiler that generates web components” Provides extra capabilities on top of Web Components: » Prerendering » Objects-as-properties » Virtual DOM » JSX » Async Rendering

Slide 46

Slide 46

Vue Supports web components as a build target. But… It still requires the Vue library as a global dependency. !

Slide 47

Slide 47

The dog ate my homework section

Slide 48

Slide 48

Using web components with a framework custom-elementseverywhere.com outlines support for many frameworks. Support is pretty solid across the board. React has some notable limitations. !

Slide 49

Slide 49

Managing Application state » Didn’t come accross any clear pattern or best pratice. » Could roll your own. » Could use any JS based state management library. » Would be nice if a default standard existsed (think React context)

Slide 50

Slide 50

My (slightly) more educated views on web components » This was pretty hard to learn! » I’d turn to this for special purpose components today. » Not yet comfortable enough for a full app/design system. » I would use a library, but one close to the vanilla API.

Slide 51

Slide 51

On an infinite timescale… » I think some version of this concept will win out. » But how infinite is that timescale? » And will it be this take on web components?

Slide 52

Slide 52

At least I’ve got this cool election tracker. Thanks! brian.perry@bounteous.com @bricomedy