Introduction to Web Components

The speaker introduces the topic of Web Components, mentioning their personal experience with the technology, its perception issues, and its position in the framework-dominated web development community.

Addressing Common Questions

A quick overview is given on what Web Components are, what they are not (specifically, they are not like React or a framework), and a humorous note on the naming convention using dashes.

Web Components' Origins and the Extensible Web Manifesto

Discussion of Web Components' history dating back to 2013, the influence of the Extensible Web Manifesto on web standards, and the significance of Web Components as a method to custom create HTML elements.

Practical Uses of Web Components

Illustrating the practicality of Web Components with an example from the education sector, emphasizing how Web Components solve the problem of adding custom elements to the web platform.

Creating Web Components

An introduction to the technical aspects of creating Web Components, starting from defining custom elements by extending the HTMLElement class.

Custom Elements and Shadow DOM

Further technical exposition on Web Components, focusing on Custom Elements and Shadow DOM, including their definitions, usage, and benefits for encapsulated styling and functionality.

Custom Elements as Helper Functions

The speaker conceptualizes Custom Elements as helper functions for HTML, emphasizing their potential to simplify coding, enhance accessibility, and ensure compatibility across different frameworks.

Encapsulation with Shadow DOM

Exploration of how Shadow DOM enables encapsulation, keeping styles and DOM trees separate to avoid conflicts and maintain a clean structure in web development projects.

Application of Web Components in Projects

Presentation of a personal project to highlight the practical application of Web Components and Shadow DOM, showcasing an interactive code runner built using these technologies.

Conclusion and Call to Action

Concluding remarks on the utility of Web Components as a key part of web developers' toolkits, with an invitation for further engagement with the speaker's work and the mention of job opportunities at Stile.

Hello, this is Web Components, another tool in your toolkit.

The reason I wanted to talk about web components, is, I feel like, I've been using them for about five years now in production, and, often when I have a bit of a weird problem, I reach for web components, and I don't see my peers do the same thing, and I think that's because it's a bit of a maligned technology, and I think it's also, there's a bit of a culture war going on with web components, particularly in the framework world.

People have strong opinions about it.

And what I want to get back to is this idea that web components are part of the web platform and you can use them.

They're another tool in your toolkit.

Let's address some confusion about web components.

There's some questions that come up when you're talking about web components.

What are they?

Really obvious one.

Is it like React?

Is it a framework?

It often gets thrown around in the same realm as frameworks and tools like React.

How come they have dashes?

And just a quick fire round, let's get through this.

What are they?

They're subclasses of HTML element.

Is it like React?

No.

Is it a framework?

No.

How come they have dashes?

I don't know.

Thank you!

that's the talk, funny joke.

Alright, so where to start with Web Components?

I think a really good place to start with this is with a little bit of history.

And I looked at the date on this and it was surprising.

2013, and some of these ideas were published a bit earlier.

The Extensible Web Manifesto.

It was a really important document and changed the way that people thought about building web standards.

And it enabled a lot of the things we've seen today with the web platform.

And there are some big names that signed this.

In fact, Alex Russell is talking this afternoon in this track.

There'sā€¦ There's some cool stuff that came out of this.

And I'm just going to read it to you.

And, it is a manifesto.

I have a Guy Fawkes mask.

We, the undersigned, advocate a change to the approach that web standards committees use to create and prioritize new features.

We believe that this is critical to the long term health of the web.

Our primary goal is to tighten the feedback loop between the editors of web standards and web developers.

We prefer an evolutionary model of standardization driven by the vast army of web developers to a top down model of progress driven by standardization.

Browser vendors should provide new, low level capabilities that expose the possibilities of the underlying platforms as closely as possible.

They should seed the discussion of high level APIs through JavaScript implementations of new features.

This involves web developers in the process and by iterating outside of the browser avoids the platform getting stuck with bad APIs.

Specifically, we offer the following design principles for an extensible web platform.

One, the standards process should focus on adding new low level capabilities to the web platform that are secure and efficient.

2.

The web platform should expose low level capabilities that explain existing features such as HTML and CSS, allowing authors to understand and replicate them.

3.

The web platform should develop, describe, and test new high level features in JavaScript and allow web developers to iterate on them before they become standardized.

4.

The standards process should prioritize efforts that follow these recommendations and deprioritize and refocus those which do not.

So that was silly.

The really important one, thank you [scattered applause].

The really important one there for this talk is number two.

The web platform should expose low level capabilities that explain existing features such as HTML and CSS, allowing authors to understand and replicate them.

Such as HTML.

And that's what Web Components is.

Sorry, I do have this note.

Web Components actually came before the manifesto.

But, don't let a good story get in the way.

Heh.

What are Web Components for?

They're for making your own HTML elements.

That's what it's for.

If you want to make your own HTML element, reach for Web Components.

And I think this is a powerful idea, this idea of being able to create your own HTML elements.

But, it's a bit of a, strange one.

What does it look like in practice?

I personally, as a science educator at Stile, I want to have a web standard input for typing maths.

That doesn't exist.

I checked MDN.

There's a big list of 22 standard inputs.

You can pick a colour, but you can't write maths on the web.

How do we do that?

How do we build that?

Someone built one, it's, this MathField component, it's an element, you write MathField, you write some funky looking, text in there, which is LaTeX, which is a thing that academics use, because they hate themselves, and then that renders down to this, which is an editable text field.

Where I can put maths in and I can type in maths like that.

And I can also open up this keyboard, and put in like advanced maths functionality.

It's a bit, complicated, but it does work really well.

And that's a web component.

And I did want to say thank you.

This is by Anno Gordal.

This is MathLive.

You can use it if you have the same problem.

And also thank you to Allie who walked out of this room, I work with her, she introduced this into the codebase, she did some really good work on it.

Because it's HTML, and this is really important, because it's HTML, we don't have to use any special tricks to get it into our app.

We can render it from the backend, we, it's just a HTML tag.

We can render it from React.

It's just a HTML tag.

We can create it inside ProseMirror, which is an editor, that likes to render to the DOM.

It's just a DOM element.

And we can render it using handlebars, which is a thing that we have to consider when we're using, new technologies at Stile because we still have, some significant portions that are in handlebars.

And importantly, it's fully composable like any other HTML element would be.

It just works in all the places that HTML works.

But, is this some fancy technology only the 1 percent can use?

No, you can use it too!

at the very beginning I said that, a Web Component is just a subclass of HTMLElement.

Literally, that's what it is.

You write class, MyCustomElement extends HTMLElement, and then you just need to tell the browser that you've done that.

With customElements.define, myElement, myCustomElement.

That's a web component.

You're done.

CustomElements, that's it.

Anyone can do this.

I'm going to talk about two things.

One, CustomElements.

This one is really cool.

Two, Shadow DOM.

This one is really confusing.

But we'll get through it.

We'll enjoy it.

Custom elements.

Here's an example of a custom element that we use on Stile.

It's for, highlighting text, for accessibility purposes, as it's read out by, a text to speech synthesis.

The highlighting is, important as a, accessibility tool.

It helps students follow along with the, audio as it's, as they're reading, and it's useful as well, particularly, for English language learners, which, we found out when we expanded into the US 30 percent of Californian students are English language learners, which is a massive portion.

So here is the DOM tree to do all that highlighting.

Unfortunately you do have to wrap every single word with a span to be able to do that highlighting like that.

You also need to wrap every sentence.

And you need to do it to basically any DOM content.

So I got into the minds of the DOM API and found out you can split elements in two and it's, wild.

But importantly, I was able to use a custom element to wrap all that functionality up.

And there's two parts to this custom element.

One is spanifying, which is cutting up the DOM into all these little spans.

And, the other I'll talk about on the next slide.

So this buildSSML function does that job of chopping it all up, and then it creates some SSML, which stands for Speech Synthesis Markup Language.

And, this has created quite a nice API around this concept of, chopping things up.

Which is handy.

And then, it does this other thing, which is highlighting.

You tell it what time you're at.

And it figures out how to highlight that span for that time.

And so then it can go through and it can highlight each word and each sentence.

And this is the API, like the surface area to, that text to speech highlighting.

It does seem a bit wild doing all the chopping up, et cetera, and, writing it as a element.

Why is it good?

I'm going to hit home with that number one point again.

The web platform is the bedrock that will work across everything.

When you deprecate React in your code base, because something new has come out, you're going to wish that you had custom elements in there.

It.

It has encapsulated functionality, so there's no need for helpers that decorate elements.

It can get really messy doing that.

And it's also much easier to operate on the DOM when you are the DOM.

This is a weird idea, but when you're part of the DOM, you can just, you can get your children elements, this.children, which is nice.

It's much nicer than querying for an element and then navigating from there.

Here's another example.

I have this thing that uses lots of memory.

But it gets added, removed from the DOM by something I don't control.

There's some other library that is adding and removing it from the DOM.

I need to know when it gets removed from the DOM so I can clean it up.

How do I do that?

If I Google that, I'm going to get told to look at Mutation Observer.

Mutation Observer is a tool for, observing mutations of the DOM.

There's a lot of code in here.

I'm going to quickly walk through it.

First, I need to get my node that I want to observe.

And then I need to decide how I will configure the Mutation Observer.

In this case, I want to know about the children and the subtree within the children.

And then, there's a callback function which takes a list of mutations and the observer that I will create, and then it goes through the list of mutations, and I have to check whether those mutations are mutations that I care about, and if they are mutations that I care about, I will then need to delete my memory.

Then I create the observer with the callback and I observe that target node.

And isn't this a bit complicated?

I already hate it.

I have a headache.

So you could do life cycle callbacks from a custom element.

When it's connected, you set it up.

And when it's disconnected, you tear it down.

This is just a part of the custom element spec.

You get a connected callback when the element enters into the document, and you get a disconnected callback when it leaves.

And, this is like a, a useEffect, the cleanup step, that disconnected callback and very easy to use.

There are a few lifecycle callbacks, there's not that many, connected, disconnected, attributeChanged.

This is helpful if you, want to have attributes that are live, and adopted.

Which is if the element is moved to a new document, which will never happen.

Just don't worry about that one.

So here's my mental model for custom elements.

They're helper functions for HTML, but there's a big gap between what I've been talking about.

And MathLive.

That was this interactive, beautiful graphical experience.

What's going on there?

The Shadow DOM.

And now we enter the Shadow Realm.

Let's look inside MathLive.

Here's something you may not have seen in your Inspector.

A Shadow Root.

What is it?

What's inside there?

What are their secrets?

If we open this Shadow Root, we will find a lot of HTML.

And this is what's implementing MathLive, the shadow DOM is where the real work happens on the math field.

From the outside, from a consumer's point of view, you, the user, of this library, you just see the math field HTML element and some LaTeX.

From the inside, It's got all of this HTML that is doing the work to make the math field look like it's real math.

It's spooky.

And unfortunately, when you use the Shadow DOM, you do have to make three Faustian pacts.

1.

Style from outside does not apply inside.

2.

Style from inside only applies inside the Shadow DOM.

3.

Elements in the Shadow DOM are concealed from DOM APIs.

And the beauty you get from those three Faustian pacts is encapsulation.

From the outside, you don't have to know anything about the internals.

And from the internals, you don't have to worry about the outside.

Your abstraction won't leak outside, and the outside abstractions won't leak inside.

But there are some terms and conditions.

Some text styles do apply across the shadow boundary, for consistency.

Things like font size.

CSS variables can transit the boundary between the light world and the shadow world.

This is useful.

You can still access the DOM nodes inside a shadow DOM.

But you have to try harder.

So I won't get into it, but you can't actually hide DOM nodes from JavaScript APIs.

Someone can get inside your web component, and it is useful to be able to, but it doesn't happen by default.

So how do we summon a shadow DOM?

It is also relatively straightforward.

You create a custom element, which we saw before.

And you, write this.attachShadow, and then you have to write mode, colon, open.

That is the only valid mode.

You just have to do that, unfortunately.

There were previously ideas that there would be other modes, but it's probably never going to happen.

Then you can style a shadow DOM.

And the way you do that is you just put HTML in it.

It's just another DOM tree.

In this case, I'm using innerHTML, but you can bring your own favorite, HTML renderer.

Any way that you can create DOM nodes, you can put that inside the shadow root.

Now inside the shadow root, I've done something a little bit spicy with my CSS.

Who's used the asterisk selector to set the background to hot pink before?

This spicy style will not escape the boundaries of my shadow DOM.

It's trapped inside.

It will not escape the shadow realm.

And then underneath that I've got a span that says hello shadow DOM.

And that span will be rendered inside the shadow DOM.

And then I've got a slot element.

And that's where the children will be placed.

Sounds creepy, the shadow DOM.

Going to put the children in the slot.

Any children nodes of any child nodes of this element will go inside that slot.

And here I have what is actually going on here.

An encapsulated element.

With HelloLightDOM inside it as a child and hot pink Hello Shadow DOM.

And this is this is a live example.

So I'm going to right click and click inspect.

And we're going to have a look at it.

The size is probably not great.

That's probably a bit better.

So we've got this encapsulated element.

And we've got the shadow root here and HelloLightDOM.

So this is in the light DOM.

It's called the light DOM by the way.

Yeah, the shadow DOM, light DOM.

Yeah, light DOM.

And here we have this style and we could change this background to green and it will work.

And that only applies within there.

And this span only exists within, the shadow DOM.

And if I go to this encapsulated element in the console, so dollar zero is just the currently selected element, so dollar zero is that encapsulated element and I do dollar zero child nodes.

There is a node list which has a single text node, which is hello light dom.

So you can't actually see the shadow DOM from the outside.

It appears to not exist.

It is concealed.

So that's a shadow DOM.

It allows you to do encapsulated elements.

So what could you do with all of that?

You've got custom elements, you've got the Shadow DOM now, you know how to use it.

What I did, was I made this thing called Runno, which allows you to run code examples on your website.

It's at runno.dev, and this is Python, running in the web browser, and it's totally gonna work.

Yes, hello world, what is your name?

It's got all these spaces because my browser resized after it loaded.

But , if I were to refresh the page, which I won't because it'll go back to the start of the slides, all those spaces wouldn't be there.

So this is Python running inside the browser, and this is a, an editor running inside an HTML element, with a terminal running inside of that.

And all you have to write as a consumer of this, API is some html.

You write runno-run and you say you want to use Python.

And then you write some Python, and that will all run inside the browser using Runno.

The summary is custom elements let you make your own elements, Shadow DOM lets you encapsulate your CSS and your HTML.

These are useful techniques that you can use And it's not a big deal.

You can create a web component.

I believe in you.

It's all part of the browser.

That makes it part of your toolkit.

Web components are another part of your toolkit.

I work at Stile.

Our mission is to create a world class science education for every student.

We're hiring.

One of my coworkers, QC, is talking in here at 2:35.

Huge plug for her talk.

Fantastic load time savings and where to find them.

You can Find me here on GitHub, on LinkedIn, and on Mastodon.

Cool?

Thank you.

Confusion

A background filled with multiple instances of the thinking face emoji.

What are they?

Is it like React?

Is it a framework?

How come they have dashes?

What are they?

Subclasses of HTMLElement

Is it like React?

No

How come they have dashes?

IDK

Thanks everyone!

A waving hand emoji is displayed in the center of the slide.

Haha. Funny joke.

Alright.

Where to start?

List of the signatories to the Extensible Web Manifesto

I'm fr just gonna read it to you

[puts on guy fawkes mask]

The full text of the manifesto. Ben reads the text.

Note: Web Components came before the manifesto, but the story works better the other way.

Making your own HTML Elements

What are Web Components for?

This is a powerful idea.

But what does it look like in practice?

I wish there was a Web Standard Input for typing maths!

(there isn't, I checked MDN)

Fortunately, someone has built one.

Illustration of a mathematical code block representing a sin function and its output below, with an arrow pointing downwards from the block to the output.

Fortunately, someone has built one.

A mathematical expression editor interface depicting an on-screen keyboard with mathematical symbols and a display area showing a math formula. Above the keyboard, there is an animation of the formula being transferred from a math-field element to the display area.

This is MathLive by Arno Gourdol

cortexjs.io/mathlive/

It's like <textarea>, but for math.

Okay, but is this some fancy technology only the 1% can use?

		class MyCustomElement extends HTMLElement {
		// ...
	}
	customElements.define('my-element', MyCustomElement);

Nope!

"Web Component" things I'm going to talk about:

  1. Custom Elements ā† This one rules
  2. Shadow DOM ā† This one is confusing

A screencast showing the transcript highlighting the current word.


class SpeechElement extends HTMLElement {
	// Public interface
	buildSSML() {
		this.#spanify();
		return `${this.textContent}`;
	}
	
	// Private internals
	#spanify() {
	// Create span elements for each word
	}

}
customElements.define('my-speech', SpeechElement);

class SpeechElement extends HTMLElement {
	// Public interface
	
	highlightWordAt(time, duration) {
		// Find the right element
		const span = this.#findSpanAtTime(time);
		span.highLight(duration);
	}

	// Private internals
	#findAtTime(time) {
		// Find the element for the time
	}
}

This seems wild, why is it good?

  1. Can be created by Handlebars, React, or anything that makes HTML
  2. Encapsulated functionality, no need for helpers that "decorate" elements
  3. Much easier to operate on the DOM when you are the DOM

I have this thing, that uses lots of memory. But it gets added/removed from the DOM by something I don't control.

How can I get notified when it gets removed from the DOM, so I can clean it up?

MutationObserver


// Select the node that will be observed for mutations
const targetNode = document. getElementById("some-id");

// Options for the observer (which mutations to observe)
const config = { childlist: true, subtree: true };

// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
	for (const mutation of mutationList) {
		if (mutation.type === "childlist") {
		console.log("A child node has been added or removed.");
		}
	}
};
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callback);
// Start observing the target node for configured mutations
observer.observe(targetNode, config);

Or...

Lifecycle Callbacks


class ExpensiveMemoryElement extends HTMLElement {
	connectedCallback() {
		// Do expensive memory setup thing
	}
	
	disconnectedCallback() {
		// Do memory cleanup here
	}
}
customElements.define('expensive-memory', ExpensiveMemoryElement);

Lifecycle Callbacks

  • connectedCallback called each time the element is connected to a DOM
  • disconnectedCallback called each time the element is disconnected from a DOM
  • attributeChangedCallback called each time an observed attribute is changed
  • adoptedCallback called each time the custom element is moved to a new document

Custom Elements

Helper functions for HTML

...but there's a big gap between this and MathLive

Inside MathLive

A screenshot of devtools with a MathLive element selected that includes a math-field tag with class attributes for styling, contenteditable attribute set to true, and a tabindex.
A screenshot devtools with a MathLive element selected showing the shadowroot.

Inside MathLive

A screenshot of devtools displaying a snippet of HTML code for a 'math-field' component with a mathematical equation inside it.

Spooooky...

A festive Halloween-themed background with a repeating pattern of illustrated skulls, jack-o'-lanterns, and ghost emojis, interspersed with spider web icons on a dark purple backdrop.

Three faustian pacts

  1. Style from outside does not apply inside.
  2. Style from inside only applies inside.
  3. Elements are concealed from DOM APIs.
Encapsulation

* Terms and Conditions may apply.

Terms and Conditions

  1. Some text styles apply across the boundary (for consistency).
  2. CSS Variables can transit the boundary (this is useful).
  3. You can still access the DOM nodes inside a Shadow DOM, but you have to try harder.

Summoning a Shadow DOM


class EncapsulatedElement extends HTMLElement {
	constructor() {
		// Summon a Shadow DOM
		this.attachShadow({ mode: "open" });
	}
}

Styling a Shadow DOM

class EncapsulatedElement extends HTMLElement {
	constructor() {
		// Summon a Shadow DOM
		this.attachShadow({
			mode: "open"
		});
		// Put HTML in it
		this.shadowRoot.innerHTML = `

		<style>
			* {
				background: hotpink;
			}
		</style>
		<span>Hello Shadow DOM</span>
		<!-- Children go here -->
		<slot></slot>
		`;
	}
}
<encapsulated-element>
Hello Light DOM
</encapsulated-element>
<[p]>Hello Shadow DOM Hello Light DOM

Shadow DOM -> Encapsulated elements

What could you do with all of that?

Runno ā†’ Run code examples on your website

runno.dev

print("Hello, World!")
	name = input("What's your name? ")
	print(f"G'day {name}, welcome to Runno.")
A screenshot of a computer interface showing a code editor with a sample Python code snippet. Below the code example is a 'Run' button.

<runno-run runtime="python" editor_controls>
	print("Hello, World!")
	name = input("What's your name? ")
	print(f"G'day {name}, welcome to Runno.")
</runno-run>

Custom Elements let you make your own elements

Shadow DOM lets you encapsulate your CSS and HTML

These are useful techniques that you can use

It's not a big deal, you can create a Web Component

It's all part of the browser, that makes it part of your toolkit!

Fantastic Load-Time Savings and Where to Find Them

Quynh-Chi Nguyen Staff Software Engineer Stile Education
2:35pm in here

Find me

github.com/taybenlor linkedin.com/in/taybenlor/ @[email protected]
A photo of a person with a cat on their shoulder.