What do you want to learn?
Skip to main content
A Practical Guide to Vanilla Web Components
by Leon Revill
Web Components are set to change how you build front-end web applications. This course teaches you how to use the Web Components specifications to create encapsulated and reusable UI components which can be used with almost any framework or library.
Start CourseBookmarkAdd to Channel
Table of contents
Web Component Fundamentals
Introduction to Web Components
What Are Native DOM Elements?
Custom Elements API and Lifecycle Callback Methods
Extending Custom Elements
Next up is extending custom elements. To demonstrate this, I created another simple project. We've got a single custom element called Division Element, which has a single custom method called divideByTen, where you can specify a value, and it's basically going to divide that value by ten and output it to the console. I've also added the new division element to the body. If we go ahead and open this simple project in the browser, we won't see anything rendered, because our custom element doesn't have any content. But what we can do is we can go and select it. Once we've selected it, we'll be able to call the custom method we've just created, called divideByTen. Can select using the querySelector and specifying the custom tag name, and then just in the same way as we would call a native method, we can call our custom method. Here we're specifying 10, 10 divided by 10, is obviously going to give us one. So what about extending a custom element then. Let's do that by extending our Division Element. Let's create another element called MultiplyElement, which instead of extending HTML element, it's going to extend Division Element. Here, instead of the divideByTen, we'll have it multiplyByTen. Same as the other method, you will be able to specify value, take that value and multiply it instead of divide by ten. Again, we need to register this element with the browser, specifying a different tag name, and also specifying the different interface. We'll also add our new custom element to the DOM so it can interact with it in the same way that we did before. If we open these changes in the browser, we can go ahead and select our new custom element in the same way that we did before, specifying it's custom tag name. Once we've done that, we can see that that we can call the new custom method we've added, which is multiplyByTen, giving us the expected value of 100, but in addition, because we've extended the division element, we now also have the divideByTen method, which is also giving us the expected output. This has demonstrated how easy it is to extend custom elements and inherit methods and properties. But what about all the native elements, which are already available in the DOM? How can we extend and build on those, and add functionality, which can be useful in our apps? This is what we're going to demonstrate next.
Extending Native Elements
Using the Shadow DOM
With the introduction to custom elements complete, it's now time to move on to the next specification, which is probably the most powerful, and that's the Shadow DOM. Using the Shadow DOM API, it's possible to create a sub-DOM tree for a specific element. That means that this element can contain and maintain its own encapsulated DOM tree. These sub-DOM trees do not inherit style from the main application, giving developers complete control over how content within these sub-trees look. When we create a sub-DOM tree for an element, there are two types available to us. There is open, which means we will have complete control over the sub-DOM tree, and closed, where it is not possible to manipulate the sub-DOM tree after it has been created. You will probably never use the closed type of Shadow DOM. In this demo, I will show you how you can use the Shadow DOM API to create a sub-DOM tree for an element. I'll then show you how you can add content to the sub-DOM tree, and finally, I'll show you the Shadow DOM in action, so you can see how its encapsulation works. To demonstrate this, we're once again, going to use a simple project. In this project we've got a couple of paragraph tags, we've got a div tag with an ID of simple.div, and we've also got a bit of basic styling, setting the paragraph tag's color to red. What we're going to do in this demo is, we're first of all going to select the simple.div element, and we're going to attach a shadow root, or a Shadow DOM to that particular element. So we'll just select it, using the document.getElementById method, and then to actually create a shadow group for this element, we just use the attachShadow method. Then in here we'll provide an object with a mode property with a value of open. As previously mentioned, open mode will allow us to manipulate the contents of the shadow root freely. Opening this project in the browser, we can see how the shadow root is depicted for this element in dev tools. For the shadow root created for this element, we can now go ahead and add some content to it, and get the benefits of its encapsulation. By using the shadow root property, we can get direct access to the shadow we just attached to this element, and we can interact with that in the normal way that we would with the element, such as using an append child or in a HTML. Here, I've just added another paragraph element, and added some new styles, setting the paragraph color to blue. Once again, opening the project in the browser, we can see that this new content has been added. Straightaway we can see the benefits of the Shadow DOM encapsulation. Because we added our new paragraph tag to the Shadow DOM, it isn't affected by the starting of the outer application, you should otherwise set it's color to red. Instead, it's color is blue, because we added some additional styling to the Shadow DOM, which it will still inherit because it's part of the encapsulated context. Additionally the other paragraph elements in the page are not affected by this new styling because the styling inside the Shadow DOM will not leak out into the outer application. These encapsulation capabilities are so significant because we can utilize this functionality in our web components. With this functionality, we can be sure that when users add our components to their applications, the look and feel of our component isn't going to be broken by any styling they added in their application. Equally, we can be confident that anything we add in our component isn't going to affect their application. Shadow DOM is a huge topic. As part of this course, you'll learn much more about the Shadow DOM than what I've just demonstrated. For those of you who would like to read more about Shadow DOM, Eric Bidelman has written another excellent article on this topic.
Using HTML Templates
Using HTML Imports
The Basic Anatomy of a Web Component
Hi and welcome to the Basic Anatomy of a Web Component module in A Practical Guide To Vanilla Book component. In the Web Component Fundamentals module I walked you through all of the work component specifications to give you a solid understanding of how these individual technologies work. In this module, we'll bring all of them together to show you how to build the basic structure of a web component. More specifically, in this module you'll learn the two different methods for writing a component and why you would choose one method over the other. I'll introduce you to some best practices for creating custom elements for your components. Dome element and tributes are a large part of the work component ecosystem, so we will look at how best to handle them. We'll look at how to define and use properties of a work component and finally, I'll show you how to use the Shadow Dom with your components. Before you start writing web components, you'll first need to decide how you want to write them. There are currently two common methods, each of which have their own advantages or disadvantages which is what we're going to cover now.
Method 2: HTML Import
Custom Element Best Practices
Now that you're aware of the different ways you can choose to write your components, we can move on to the specifics. Next, I'll be showing some best practices for creating your web component's custom element. In this demo we'll take a more detailed look at how the custom elements class we use to define our work components. I'll show you some tips for naming the class and element tag, on implementing custom methods, how to best define local variables, and best practices when updating your component's template. In this demo we're going to create a random quote component which will simply print out a random quote every 10 seconds. This simple example will allow me to share some best practices I've discovered while working with web components. The first thing I'll do is define our class and define the custom the element. When choosing a name for my class, I always use camel case and be as descriptive as I can while not making it too long. Then when choosing the element name, I always convert the camel case class to a hyphen telemeter string. When creating a custom element, your element tag must have a hyphen in it to distinguish it from the native elements so this works perfectly. Notice that I added another view in front of my class and element name. This is because your element name has to be unique. If we called our component simply Random Quote, it is very likely that someone else will do or has already created a component with that name. This means that our component and their component couldn't coexist on the same page. So it's important to try to keep the element name unique by adding a prefix. I've chosen RW for Rebel Web, but you'd use something else. Think of it as a kind of name space for your elements. Next I'll create the class constructor calling super as always and then I'll define some private local variables. ES2015 classes don't have the concept of declaring private variables but you can intact script over the languages but there is an unwritten rule that prefixing a variable or method with an underscore means that it's private and probably shouldn't be used externally. With that, I can create a private course array with three different quotes. A quotes variable which is going to store a reference to the Dom which will display the quote text. Know that here I also prefix it with a dollar sign. I do this with any variable that holds a reference to a dom element so it's easy to see what type of data you are dealing with. By looking at that variable, you'll know instantly that you'll have access to methods like query selector or the methods and properties at least part of the HTML element interface. Next, I'll add an interval variable which will store the interval instance used to update the quote every 10 seconds. We'll now move on to using the connectToCallback method to stamp the component with our initial templates. Here we're just displaying a title and providing an element to display the quote text. I've also added some CSS for some very basic styling. Because I'm not using the shadow dom for its encapsulation in this component, I'll need to prefix the class names to limit the chance of a conflict. With the initial template added, I'll pull the reference to the quote element out and configure the interval. The set interval function will call a random method which we haven't created yet. With the interval configured, we'll also need to initially call the random method. Now we'll move on to creating the random method. The random method is a custom method on our class. We will prefix it with an underscore identifying it as a private method and that it shouldn't be used externally. Then, all we'll do is take a random item from the quotes array and update the inner HTML of the quote element with the quote text. Notice here that we're only updating the part of the dom which needs to be changed by individually selecting the quote element. What would actually be easier for us to do is just restamp the entire template every time we want to update the quote text. This would be very inefficient now. Always remember that dom updates are expensive and to only update the parts we need to change. To finish off the twelfth component, we'll use the connecttoCallback method to get the interval that it won't continue to execute if the element has been removed from the dom. Here you can see that I've already included this component into index.html so we can go ahead and take a look at it working in the browser. Here we can see that the initial render has chosen the first random quote and updated the template. Then after 10 seconds we can see that the contents of the quote element will be updated again calling the random method from the interval. These tips and best practices I'm sharing with you are guidelines only. Don't feel you need to follow them religiously. Once you start building web components more frequently, you'll most likely discover your own style and new best practices.
Web Component Attributes
Attributes are one way your web components can receive data and publish date. In this section, I'll show you how easy it is to handle attributes on your components. For this demonstration, I'll be building on top of the random quote component we built in the previous demonstration. Just as a reminder, this element simply takes a random quote from an array and prints it in the component template every 10 seconds. In this demo I'll show you how you can make the interval configurable by using attributes and also how we can use an attribute to share which quote is currently being displayed. The first thing today is to create a custom method called SetInterval. This will give us a reusable way to update the render interval. This method will take the interval value in milliseconds. It will first cancel any variable instances using the clear interval method. Then if the specified value is greater than zero, it will configure a new interval. The same writing the code again, we can just reuse line 32 from the previous version of the component. To access the element's attributes, we can use the GetAttribute method. We'll call the setInterval method from the connecttocallback with any value from the interval attribute. This way if there is an initial interval attribute specified in a set interval, will be configured with the specified value. If we jump over to index.html and then specify an initial interview value, we can then go ahead and open this in the browser and we'll be able to see that the initial interval which is now configured is set to one second, but if we change the value at run time, it won't change the interval because we're not yet watching for those changes so let's go and do that now. If we want to observe the tribute changes, you need to specify the serveAttributes method and provide an array of the attributes we want to observe. In this case, that's just going to be the interval attribute. Next, using the attribute change callback, we can now be informed when the interval attribute value gets modified. When the interval attribute gets updated, the attribute change callback method will get executed and then we can now use the new value variable to call our set interval method with the new value. Loading this back in the browser, we can then try it out and see that it works. We can also use attributes to provide information about something happening inside the components. As an example, we can add an attribute to our component when a new quote is displayed. It is as easy as using the set attribute method. There are many reasons why you'd want to use attributes to display information about a component. Think of how native elements work and disable a tribute is used to identify import which is read only or a check attribute is used to show a check box has been checked. Back in the browser, we can see that the current index attribute gets update with the index of the quote every time it changes.
Web Component Properties
Properties are another way your components can receive data. Properties can also be used to distribute data outward. In this demo I'll show you how we can create an API of your component using properties. I'll describe the difference between properties and attributes. I'll demonstrate how you can allow data to be pushed in to your component and I'll show you how getter methods can be used to allow data inside your components to be retrieved externally. Using attributes to provide data to your components, it's perfect for simple data, but ultimately attribute values can only be strings and therefore attributes are not suitable for more complex data like objects and arrays. For this demonstration, I've prepared a poll work component which presents the question to the user and allows them to choose a single answer. Before we get started, I'll quickly walk you through how this component works. In the constructor, I've declared a few private variables and set some initial data to display. In the connecttocallback, I first set the attached variable to true so we know when the element has been added to the dom then I add the component template and store some references to the important elements. I've added an event listener to the answers list so when a user clicks an answer, it will find the index storing it in a private variable and also adds the selector class to mark the chosen answer as green. The last thing in the connectocallback is calling the render method which updates the component template for the data in the data variable. Currently, this component is just displaying some static data which in practice is pretty useless. How can we create an API so someone using this component in their app can not only provide it with a new question and answers but also programmatically select the selected answer. This is where properties come in. First and foremost we want it to be possible to dynamically add a question and a set of answers so we'll remove the static data. To define a property and a custom element, we will use setters and getters. We'll start by adding a data setter. A setter takes only a single argument which is the value being set on a property. As a rule, I always check to see if the data being provided is actually new to avoid doing needless work. If there is something new, instill the data to a private variable and then call the render method to update the template with the new data. We also want it to be possible to get up the data which is currently set and we can do that with a getter. A getter has no arguments. Here we'll simply return the value of the private data variable. Okay, we also want it to be possible to programmatically set the selected answer as well as allowing it to be selected by the user. To do this, we'll use another setter. This setter takes an index of the item to be selected. It then uses the career selected method to select that specific child from the list. It removes the selected class from any of the list items which might have it and then adds a selector class to specify an item. Finally, it then stores the selected index in a private variable. To expose the selected index, we can then use another getter to just return the value. To finish this component off, we can remove the duplicated logic from the answers event listener and just call the selected property internally. With the component open in a browser, we can now try out the properties we've just added. It's not rendered anything in the dom yet because it doesn't have any data so let's give it some. Once the data is set, we can also print it out. We can still make a selection as a user but we can also use a selector property to dynamically change it and we can also use the selector property to print out the currently selected value.
Web Component Shadow DOM
The shadow DOM is one of the most powerful features of work components. In this section, I'll show you how to actually implement the shadow DOM in the work component. Getting the encapsulation benefits of the shadow DOM in your component is very easy and in this demo, I'll show you how. I'll demonstrate how easy it is to create a subdom tree for your work component and I'll also show you how to add your component's template to this subDOM tree and also how to update it. In this demo we're going to modify the pull component we created earlier to gain the benefits of the shadow DOM and it's going to be really easy. We've already seen the shadow DOM provides style encapsulation. To demonstrate this working with the poll component, I'm firstly going to add some conflicting styles to the index.html forcing all of these elements to have a black background. Currently in the poll component we are always referring to the custom element's direct children, ie when we add the template and also when we select the question and answer elements. When we introduce the shadow DOM, we'll need to place the template in the subDOM tree and also select the elements there too. In the constructor, we'll attach a shadow route to this element making sure it's in open mode and store the reference to the shadow route in a private variable. Refreshing the component in the browser will show us a shadow group that has been added. Now all we need to do is make sure we interact with the shadow route instead of the element's direct contents. This is where we use the route variable. In the browser we can see that our template is now within the shadow route. Adding some data will also place the content within the shadow route because we updated the references to the question and answer elements. You'll also notice that our list items now have the desired background color because the shadow route of our work component won't inherit the main document styles giving us complete control over what our component looks like.
Web Component Skeleton
And that concludes this module. All of the concepts I've shown you during this module come together nicely to provide a work component skeleton which you can use as a starting point for all of your work components. It shows how to declare private variables, create a shadow route for your component, where to add an initial template, to store a reference to important elements for later use and the prefix DOM elements with a dollar sign and how to declare private methods and the best practice for selectively updating template content, how to observe certain tribute changes and react to those changes, how to create an API for your component with properties and finally, best practices for element and class naming. This module has introduced you to the two principle methods for writing work components and presented the advantages and disadvantages of each. It's provided you some best practices for creating custom elements for your work components. It's walked you through handling attributes for receiving data and advertising states. It's demonstrated how to use the component properties to distribute and receive data and finally showing you how to gain the shadow DOM's encapsulation methods within your work components but most importantly this module has solidified your understanding of the individual work component specifications and taught you how they're used together to build web components.
Creating a Star Rating Component
Hello and welcome to the Creating a Star Rating Component module in A Practical Guide to Vanilla Web Components. In this module, I'm going to walk you through building a high quality star rating web component which will provide a practical demonstration of all the concepts you've learned through this course so far. This module will be focused on taking you through the process of building a high quality web component. A high quality web component is a web component which strives to be as functional and robust as a native element. We will be analyzing how current elements work to decide how our custom element should behave. I'll demonstrate some Shadow DOM styling capabilities to help in our component design. The star rating component will make use of multiple attributes for different purposes, so I'll show you how to handle them, and finally, our component will need to emit events to inform users when its value has changed. By the end of this module, you'll be well on your way to having an in-depth understanding of web components and how to build them. Before we get started in building our star rating component, I'm first going to demonstrate the end product. In this demo, I'll briefly show you how the component is going to look, how it's going to behave, and also its API, in terms of what properties are available. The star rating web component we're going to create is a simple component that will present the user with five stars they can choose from. As you'd expect, when a user hovers over the element, the appropriate numbers of stars will be highlighted and will allow them to make a selection. The component will also have a value attribute, allowing for an initial value to be specified. It will also have a disabled state so that it can be used to display read-only data. And finally, it will have a single-value property to provide the ability to programmatically set and retrieve its value. When building web components, it is very important to realize that there are a large number of super-performance, well-tested elements already available in browsers. Years of development has gone into some of the native elements, with millions of people using them daily. Web users are very familiar with these native elements. They'll have expectations of how certain types of elements should work. When you build the web component, you should look to native elements and take inspiration from how they work and strive to match this behavior as much as possible. This is exactly what we're going to do with our star rating component. When building a web component, try and pair it with a native element, if there is one. Take our star rating component, for example. It takes user input, so it's a type of input element. Evaluate stores as a number, so it's basically a simple number input with a fancy UI. That means that we don't have to spend lots of time trying to figure out how the star rating component should behave in subtle situations. We can learn from the native number input. Before we can do that, though, we need to understand exactly how the number input works.
The Number Input
Creating the Foundation of the Star Rating Web Component
Now that we've got a good understanding of how we want our element to behave, we can get to work building our star rating web component. In this demo, we're going to create the very basic requirements for our star rating custom element, give it a shadow root so we can go in the Shadow DOM encapsulation benefits, we'll add an initial template, along with the basic styling for the five stars in the component template. We'll start off by creating our class called Rw Star Rating, which extends the HTML element interface. The element name, as always, will be the class name separated by hyphens and there we can just specify the class we just created. The constructor will create a private variable. Storing a reference to the shadow root will create for this element, and also some private variables for important template element references. Next up, within the connector callback, we'll stamp an initial template. We have a container with a top and bottom section. The top section holds the gold stars, which will become visible when a value is set by setting an appropriate width on this element. The bottom section holds the gray stars underneath. We add a data set value for each of the bottom stars so when one is selected by the user, it's easy to get the selected value. You may have also noticed that these values are in the wrong order. I'll explain this shortly. Moving on to the CSS, within a shadow root, we can use the host selector, which refers to the host of the shadow root. In this case, that's our star rating element. This means we can apply styles directly to the custom element from within the shadow root. Next, we add some basic styling to position the container and its sections correctly. And finally we'll add some magic, which will give us the correct hover effect for the gray stars in the bottom element. From the template, we've seen that we have a top section of stars, which will sit above the bottom section using a zed index. When a value is set, either by the value attribute, the value property, or via a user selection, the value will be multiplied by 10 then doubled to give us the correct width percentage to be applied to the top section. The bottom section is a little trickier, though. What we want to happen is, when the user hovers over the stars, current star and all preceding stars are highlighted. The problem, though, is that there is no way in CSS to select the preceding elements, but there is a way to select the succeeding elements. If we did that, though, the opposite stars would get highlighted. To fix that, we can just change the element direction from left to right to right to left. Here you can see the CSS, which selects the succeeding elements and the CSS property which changes the direction of the bottom stars. This is why we need to put the data set values in the opposite order. I've already added the web component index to HTML so we can take a look at it in the browser. We can see that it's rendering the five stars in line with the text, and when we hover, we get the effect we're hoping for.
Implementing the Disabled Mode
Next, we're going to implement the disabled mode for our star rating web component. We'll use some CSS to prevent hover and change the user's cursor. We'll utilize a private variable to keep track of whether the element is disabled or not, and we'll watch the attribute changes so we know when the element should be put in disabled mode. The CSS we add first resets the cursor so the user doesn't think they're able to click the element in disabled mode. Then it just cancels out the display and none property in line 43 because we want the top layer of stars to be visible on hover for disabled mode, and it also cancels out the hover effect of the bottom stars. Using the host selector, along with square brackets, we can specify the disable to distribute, which means these CSS properties will only get applied when our custom element has a disabled attribute. Next, we'll create a private disabled variable. In the connector call button method, we'll set this variable to true if the disabled attribute is present. Otherwise, it'll be false. We also need to observe changes of the disabled attribute, so it can update this variable if the disabled attribute is added or removed at one time. We do that by specifying that we want to observe the disabled attribute, and then use the attribute change callback to update the private variable accordingly. I'm using a switch statement here because we'll be adding an additional attribute observation later. If we go into index to HTML, and add a disabled attribute to our component, in the browser we can see that the hover effect has been disabled and that our cursor reflects the disabled state. Additionally, thanks to the attribute change callback, if we remove the disabled attribute at run time, the component falls back to its default behavior.
Adding a Value Property
Next up is the ability to programmatically set and get the component value. We'll use a private value variable to hold the data. We'll create setter and getter methods to set and retrieve the data. And we'll create render method to update the template to reflect the new value. We'll start out by declaring the private value variable, which will hold the element in its current value. Then, in the connector callback, we'll create a reference to the top element, so we can update its width within the render method. We do that by simply using the query selector method on the reference to the shadow root to pull out the element with the top class. The render method is a custom class method which simply takes the value, multiplies it by ten, then doubles it to give us the required width percentage. And using the style property, you can set the width of the top element accordingly. The setter method, as always, will first check to see if the value being provided is actually different to the current value. If it is, update the current value and then call the render method to update the template. The getter method simply returns the current value. Opening our star rating component in the browser by selecting it from the DOM and then using the value property, we can set its value and it'll be reflected in the template. Also, we can use the value property to get the current value.
Allowing a User Selection
Now we've implemented the render method. We can let the user make a selection by clicking one of the stars. This will then update the component value based on the star they selected. And based on what we learned about how the number input works, we should also dispatch a changed event. We're going to need to listen for clicks on the bottom stars, so first, we'll need to create a reference to the bottom element. Again, we do that by using the query select method on the reference to the shadow root, selecting the class. Then, by adding a click event handle to that element, we'll know when one of the stars has been clicked. We don't want to perform any action if the element is in disabled mode, so we'll first ensure that the disabled value isn't set to true. Then we'll make sure that the element has a data set value, i.e., this is one of the bottom star elements. Finally, if the current value is not the same as the just selected value, we'll then dispatch a change event and also we'll set the value property to the new selected value. In the browser, we will first attach an event listener for the change event on our star rating component. Then when we make a selection, we can see that that event is getting fired and that selection is being made. The final implementation detail is that if we make the same selection twice, the change event isn't fired because the value's not actually changed.
Handling the Value Attribute
The final part to our star rating component is the ability to add an initial value using the value attribute. We need to observe changes to this attribute, then update the component's value based on this data, and to match the behavior of the number input. We should only do this if the value hasn't already been set by the user or by the value property. The first thing to do is check the initial value of the value attribute within the connector callback. If there is a value, then update the private variable and call the render method. There's a reason why we don't just use the value property here, which you'll see shortly. Before we go any further, we need to declare a new private variable called touched. We'll then set this variable to true within the value setter so that if a user makes the selection or a value set by the value property, we'll know they want to ignore any value attribute changes. This is why we don't use the value property in the connector callback for the value attribute. Next, we add an additional string to the return value of the observer distributes method, specifying we want to observe changes for the value attribute. And then we add another case to the switch statement. We're at the touched value is false, we'll take the attribute value and update the private variable once again calling the render method. And that completes our star rating web component. To see this last bit of functionality working, if we set a value attribute on our component, and open it in the browser, we can see that the three stars is reflected on initial render. If we change the value attribute, we can see it gets updated. And finally, once we've made a user selection, the value attribute changes are no longer reflected in the UI. This module walked you through building a high quality web component step by step, taught you how to analyze native elements to better understand how custom elements should be implemented, demonstrated how to harness some of the Shadow DOM styling capabilities with the host selector, provided insight in how to handle multiple attributes in a single component, and showed you how to dispatch events from within a web component. The web component built in this module is a practical example of a web component which will be very useful in real projects and should give you more confidence to experiment with the OMA components using techniques learned in this module.
Creating a Configurable Slide out Menu Component
Hello, and welcome to the Creating a Configurable Slide Out Menu Component module in A Practical Guide to Vanilla Web Components. In this module, we're going to build a more complex component, and I'll demonstrate some additional features of the Shadow DOM. In this module, I'll be walking you through building a slide-out navigation menu component. We're going to add some basic built-in color themes that the component user can easily apply, and we'll do this by using CSS, so we don't have to observe attribute changes. We'll use a property to set the menu to open or closed, and I'll introduce you to another feature of the Shadow DOM called slotting, which allows users to project their own content into the Shadow Root of your Web Component. Before we get started in building our slide-out menu component, I'm going to show you the completed component in action. I'll show you how it works, what configuration options it has, and how it is constructed in terms of the DOM structure and important bits of CSS. The slide-out menu component has a custom element of rw-slide-menu. It slides out from the left-hand side. The menu itself has a title and content section. It also has a backdrop which, when clicked, closes the menu. The backdrop can be disabled using the backdrop attribute, setting its value to false, which then allows for items behind the menu to be clicked. A theme can also be applied to the menu, using the theme attribute. There is a blue and red theme available. The component template consists of a frame element, which covers the entire browser viewport. the frame element contains nav element, which is positioned off the left-hand side of the screen, using the translateX transform. When the open property on the component is set to true, an open class is added to the frame element, which removes the translateX transform. The nav element has a will-change transform property, which promotes the nav element to a separate layer. This prevents the browser from redrawing the rest of the page when the menu is slid in or out. This will increase the animation performance, especially on low-cost or older mobile devices. Because the frame element is an overlay to the entire browser viewport, we need to stop that element from being clickable, otherwise a user wouldn't be able to interact with the application behind the frame element when the slide-out menu is closed. We can do this using the pointer-events CSS property, and setting it to none. Then when the menu's opened, we can set this property back to auto, which will then allow the user to interact with the menu and backdrop.
Creating the Foundation of the Slide out Menu Web Component
Now we've got a rough idea of what we're aiming to build, we can get started. In this demo, we'll create the component class and define the custom element, we'll add the initial template, and we'll also add the template CSS, which will include the slide-in animation. I've already created the component class and defined the custom element. Next, I'll create the class constructor, which will create the Shadow Root, and some private variables which we'll use later. The first private variable will hold a reference to the frame element, and we'll also create a private variable which will hold the open state. In the connectedCallback method, we'll stamp an initial template into the Shadow Root. In here, we'll add the frame element, which covers the entire browser viewport, then a nav element with a container class, next, the title, which contains a content area and a close button, and finally, the menu content area. Inside here, just for now, I'm going to add some basic static content. Moving on to the CSS, we'll first add some for the frame element. This CSS ensures the frame covers the entire viewport, sets pointer-events to none, so the user can click through it, sets a high z-index, so it's above all other elements, and configures a background-color animation used for the backdrop. Next is the nav element, which has the container class. We use the translateX transform to slide if off the screen by default, and use the transition to ensure the sliding effect is animated. The will-change transform property is added here to promote this element to a new layer. The CSS for the title and close button is pretty self-explanatory. And finally, we add some CSS for when the frame has an open class, which resets the pointer-events, allowing the frame and nav element to be clicked, and also removes the transform on the nav element, sliding it back in. Now we have the very basic foundation to our slide-in menu component. Let's try it out. We currently have no way of opening and closing our menu, but we can simulate it by adding the open class to the frame element. When we do so, we can see that the nav element slides in from the left. Here, we can see the title, close button, and the pretty ugly content area. If we then remove the open class, we can see that the nav element is then transitioned back off the screen.
Styling the :host
Adding Themes to the Slide out Menu Component
Allowing the Menu to Be Opened and Closed
Now it's time to add the main feature of the slide-out menu component, which is the ability to be able to open and close it. In this demo, we'll define the open property, which will allow the menu to be toggled open and closed, we'll ensure the menu is closed when the user clicks the backdrop or the close button, and we'll add the CSS which allows the user to disable the backdrop. We're going to use a property to allow the user to set the open state of our component. We've already created the private variable to hold the Boolean state. Now let's just add a setter and a getter for the open property. The setter will take the value and determine if it's a Boolean value. This ensures that if the user passes in something other than a Boolean, it will just default to false and not throw any errors. As always, if the specified value isn't different to the value we currently have, just return. We then update the local open variable, and then call the render method, which we will create shortly. The getter simply returns the private open variable. There are two ways to close the menu: via the close button, and by clicking the backdrop if it's enabled. What you could do here is add an eventListener to both the frame element, i.e. the backdrop, and the close button. But I like to use as few eventListeners as possible. We first need to store a reference to the frame element, which we can do in the connectedCallback. Then add a single eventListener to the frame element, which will use the event object to determine if the clicked element has a close dataset item. If it does, we set the property to false. To make this work, we need to add the close dataset property to all elements which can close the menu. In this case, that's the frame, i.e. the backdrop, and the close button. Next, we'll add the render method. First, let's ensure the frame element is available, i.e. we've been connected to the DOM. If open is true, then we need to add the open class to the frame element. Then we'll dispatch a CustomEvent called menu-opened, informing any users that menu's just been opened. If open is false, then we'll do the opposite. We'll remove the open class, and dispatch the men-closed event. To finish off, we'll add the CSS which allows the user to disable the backdrop of our attribute. Once again, we use the :host selector, specifying the attributes and the value. Before we open this in the browser to see the functionality we've just added, I first want to show you what I've added to index.html to help us demonstrate this functionality. The first thing I've done is add a few buttons, one to open the menu and one to close the menu. Both of these buttons are all within a division element with a class of controls. Then I add a click eventListener to the controls element, so I can determine which button has been pressed. If the Open Menu button has been pressed, I then set the menu.open property to true. If it's the Close button, then I set the open property to false. I've also added some eventListens to the menu itself, one for menu-opened and one for menu-closed. Now I can go ahead and open this in the browser, and see if it's all working. So the first thing we're going to do is press on the Open Menu button, which you can see is working as expected. And then the console will be able to see that the menu-open has been fired. If I then close the menu with the Close button, we can see that that's worked, and the menu-closed event has been fired. If I open the menu again, and click on the backdrop, we can see that that also closes the menu. Now let's demonstrate that we're actually able to disable the backdrop using the backdrop attribute with a value of false. Now when we open the menu, we can see that the backdrop isn't present, allowing us to click through to the buttons and close the menu.
Allowing Users to Provide Component Content with Slotting
We've nearly completed the slide-out menu component. To finish it, we're going to use another feature of the Shadow DOM. This feature is called slotting. Throughout this course, you will have come to learn that Web Components are all about encapsulation, particularly in relation to the Shadow DOM, the idea being that a Web Component shouldn't affect the outer application, and the outer application shouldn't affect the contents of the component. But what about components which require content from the user to be useful? A modal, for example, is useless unless the user is able to specify the content they want to display inside the modal component. Our slide-out menu is exactly the same. We want our users to be able to specify the content they want inside the menu. This could be any HTML, an image, some text, a few buttons, or a list. Slotting allows us to do exactly this. As a component user, we can add content as child elements to the Web Component, and then as a component author, using slotting, we can choose where this content is placed within the Shadow Root of our Web Component. The slotting feature projects the content into the Shadow DOM, making the elements appear to be within the Shadow Root of the component, but are actually kept within the Light DOM of the component. This means that the usual styling restrictions of the Shadow DOM don't apply to the slotted content. This is a huge benefit, because component users will be able to style the content they've provided to the component, just like they would style any other DOM element which was outside of a Shadow Root. In this demo, we'll add slots to the component template for both the menu content and the menu title, we'll also add some default style for slotted content, and finally we'll specify some fallback text for the title slot, to ensure there's always a title for the menu. The first thing we're going to do is replace the static content we added earlier with a slot element. We're also going to give this element a class, so we can style the slotted content later. The slot element allows the component author to decide where the slotted content is placed with inside the template in the Shadow Root. Next, in index.html, we'll add some content that we want slotted inside the component. As we've been building a menu, it only seems appropriate that we'll add some anchor tags. I'll simply add three, each with the text One, Two, and Three. Next, we'll open it up in the browser, and we can see with the menu open that all three anchor tags have been placed in the appropriate place. In the element explorer, when we drill down to where the slotted element is, we can see the projection at work. When we click on each of these slotted elements, we can see that they are revealed in the associated place in the Light DOM. You've probably noticed that the slotted content looks pretty ugly in its current form. We know that the user can style this content however they like, because it's still in the Light DOM. But as component authors, we can still add some default styles, so that the slotted content looks good straight out of the box. Within the Shadow DOM, we have a new CSS pseudo selector available to us, which will allow us to add style specifically to slotted content. We can specify tag names. In this example, we are styling all slotted anchor elements whose parent has the class content-slot. In the code we've just written, this is the slot element. We can use other pseudo selectors, such as the hover selector, and we can also specify classes. This all seems great, and is incredibly useful, but there are limitations. You can only style top-level items. If you were to do something like this, it just wouldn't work. It's also important to understand that user-specified style will always take precedence over slotted style which is added inside the Shadow DOM. In this example, on the left we have our slotted content style added within the Shadow DOM of our Web Component, and on the right we have the user-specified style for the slotted content they added to our component. The user-specified styling would always win. Now we're aware of the slotted pseudo selector, we can use it to add some default style to the slotted anchor elements. The styling I've added here is self-explanatory. It ensures the anchor elements are displayed on top of each other, have a bottom border, and also change color when hovered. We also need to update the hover color based on the theme, so I'll do that now. First, we ensure the hover color is red for the red theme, and then blue for the blue theme. Back in the browser, when we open the menu, we can see that it looks much better. The default styles have been applied to the slotted anchor elements. When we swap to the red theme, we can see the hover style updates, and the same for the blue theme. To further illustrate my point I made earlier about user-specified styles taking precedence over the slotted styling within the Shadow DOM, if I add some styling in the document which changes the anchor color to red, we'll be able to see that this styling wins over the styling we added inside the component. Currently, in our slide-out menu Web Component, we have a single slot element. Any child elements will be projected at this location in the Shadow DOM. But what about having multiple places in your templates that you want to be able to project content into? With our slide-out menu component, we have that exact problem. Not only do we want users to be able to add menu content, we also want them to be able to specify the markup for the menu title. This is where name slots come in. To do this, it's actually really easy. If we replace our menu text with another slot element, except this time we give it a name, give it something meaningful, so in this case title, then what we can also do with slots is we can provide some default content, so that if a user doesn't specify a value, we have a reasonable default. We can demonstrate that now by going straight to the browser, and what we'll be able to see is that the menu text is still displayed, even though we haven't added any content to be slotted. And we can see inside the slot element, it's defaulted to the default text. Now if we go back into our project, and open up the index page, and actually add some slotted content there. So we'll add a h3, which is appropriate for a title. And the way we specify for it to go in the additional slot element is by giving it a slot attribute, and then specifying the name we chose. In this case, that's the title. Back in the browser, we'll be able to see that the menu now holds our new slotted content. And that concludes our slide-out menu Web Component. In this module, we've built another Web Component step-by-step, which is our slide-out menu component. We used the :host selector to add style to the component based on its attributes. In this case, that was with theming. We learnt about the :host-context selector. You were introduced to the slotting capability of the Shadow DOM, which included default slots and name slots. This allows component users to project their own content into the Shadow DOM of your Web Components, while still giving the component author complete control over where that content goes. You were also shown how to add default style to slotted content using the ::slotted selector.
Styling Web Components
An Introduction to CSS Custom Properties
Hello, and welcome to the Styling Web Components module in A Practical Guide to Vanilla Web Components. The course so far has demonstrated multiple times the power of using the shadow DOM to encapsulate style within a component, to prevent the outer application from affecting the component internals. With this capability though comes some interesting problems. A component user needs to be able to adapt the look and feel of a component so it fits into their application. This module focuses on how component authors can create a styling API for their components, providing flexibility to their users while still leaving control in the hands of the author. In this module, I'll give you an introduction to CCS Custom Properties, which forms the basis of the web component styling capabilities. We'll create a styling API for the star rating web component we created earlier in the course. We'll also apply what we learn in this module to the slide out menu component we created previously so users can easily change various visuals in the component, and I'll show you the possibilities of using CSS @apply mixins to make theming your components even easier. Before we start thinking about creating the styling APIs for our components, I'm going to give you a brief introduction to CSS custom properties and also give you an overview of which browsers support this feature. To demonstrate CSS custom properties, I'm firstly going to need something to use them on. For this, I'll quickly create four squares in the center of a page. I'll add some CSS to remove any margin and padding from the body, and then some CSS to position the container and boxes in the center of the screen. Refreshing the browser, we can see the four empty squares on the page. Now we're going to use CSS custom properties to color them in. If you're familiar with CSS variables in a CSS pre-processor, like SASS, then you're already familiar with CSS custom properties. Except they are a native CSS feature with slightly different syntax. The principle is simple. At a high level, we can declare some custom properties depicted by the double hyphen, and also assign some values to them. In this example, I'm going to declare some colors I'd like to use throughout the application. We have primary color, secondary color, and border color. With those custom properties defined, let's use them to color in the boxes. Let's make one and four the same. To use one of the custom properties, we use the var keyword, specifying which custom properties to use. Here, we'll set the background and border color accordingly. Refreshing the browser, we can see that it's working. Next, I'll use the same principle to color the boxes two and three, this time using the secondary color and gray for the border. Another refresh, we can see each box is colored in. The idea here is that we can declare easily reusable variables which are not limited to color. You can declare variables for font families, text sizes, padding, all sorts of things. We can also use CSS custom properties to pierce the shadow DOM, which is exactly what we'll be doing with our components. If you're not already familiar with CSS custom properties, then you might be surprised by the wide support already available. Using caniuse.com, we can see which browsers already support the feature. As you might have expected, Chrome has got full support. Safari 10 has got support for this feature, as well as iOS Safari, the Android browser support set, and so does Chrome for Android. Edge is currently in development and it shouldn't be much longer until we have this feature natively, and there are polyfills available if you want to use CSS custom properties with Internet Explorer.
Adding a Styling API to the Star Rating Web Component
Now you're familiar with CSS custom properties, I can show you how we can use them in our web components to allow users to style parts of the template even within the shadow DOM. In this demo, we'll create a styling API so users can choose the color of the stars in their default, hover, and selected states. I'll show you how you're able to specify fallback values for properties using CSS custom properties. And we'll take a look at how a user can specify CSS custom properties for the component to use. Before we get started, just a quick refresher on what the star rating component is all about. It's a very simple component that allows you to make a selection of one to five stars. When the stars are hovered over, they change color to show the potential selection, and when selected, they keep the gold color. The first thing we're going to do is allow the user to change the default color of the stars, which is currently gray. To do this, we need to change the value of the color property on line 37 to be a custom property instead of a static hex value. Remember that the custom properties you create are basically the styling API to your component. To ensure they have a descriptive name, I'll choose star-default-color. The problem now, though, is that if a user doesn't provide a custom property for the component to use, then the stars' color will fall back to the browser default, which is not the color we want. To fix this, we can provide a second parameter to the var function, which is the fallback value. Now if we refresh, we can see that the gray color is retained instead of the browser default, which would have been black. Next, we'll do the same for the selected color, again choosing a descriptive name for the custom property and using the current hex value as the fallback value. A refresh shows us everything is still using the default colors. The last custom property to add is for the hover state. Using the same process again, let's just quickly add it. Another refresh, and it's still all looking good. That's how easy it is to expose certain properties, which will allow the user to provide new values for them, even from outside the shadow DOM. But how exactly does a user do this? If we head over to index.html, we can do that now. To provide values to these custom properties, we just need to declare them on a parent of the component. We could add them to body, but instead, assuming that these custom properties will only be applicable to the star rating web component, we'll add them to the host element using the element name as a selector. We'll first declare a new value for the default color. Refreshing the browser, we can see that the stars are now a darker gray. Next, let's make the selected color red. Another refresh, and we can see that when selected, the stars change to red. And finally, if we set the hover color to blue, we can see that each color has now been modified using the CSS custom properties.
Adding a Styling API to the Slide out Menu Web Component
The star rating component is a very simple component with little that a user would want to change. The slide out menu component is much more complicated. In this demo, we'll create a styling API so that users can customize the various colors the menu uses, change the size of the title, also change the width of the menu, which includes the ability for it to be 100% wide. Once again, just a quick reminder of what the slide out menu component does. It's a menu component which slides out from the left, and via slotting allows the user to specify a title and some content. Because slotted content is projected into the shadow root but captured in the light DOM, then the user has full styling capabilities of this content. This means that we won't be creating CSS custom properties to style this content. Instead, we'll be adding properties for styling shadow DOM internals. The first custom property we will add is for the menu width. Currently, this is set to a static 80%, and I'm sure users would like to change this based on their application. Instead of the static width, then, we'll declare a custom property called menu width, remembering to give it a descriptive name and a default value of 80%. This ensures that if a user doesn't specify a value for this custom property, we fall back to the originally intended width. We'll also need to remove the max width property, otherwise the user won't be able to set anything greater than 400 pixels. With a quick refresh, we'll just double check that the default value is still being used, and we can see that it is. Moving into index.html, let's add a value for this property and see if it works. I'll set 100% width so we can cover the entire page with the menu. A quick refresh, and now when the menu opens, we can see the width is as we specified. Okay, so the next property we're going to add is going to be for the title size. This will basically allow the user to specify the font size of the title. We'll give it a descriptive name again, title-size, and specifying the default value for it to fall back to. We'll just double check that the default fallback is still being used, once again it is. And then we can move into index.html to specify a new value. In this case, let's make it smaller than the default. So let's use 0.8em. Save that and refresh the browser again, and we can see that the whole title has been resized. So we already have themes available for this component. We've got a red theme and we've got a blue theme. But these themes are obviously quite limited, so let's move on to adding some more custom properties that allow the user to have more control over the colors used inside the menu. To do this, we'll use the same process once more, specifying custom properties for the values we want the user to be able to change, starting with the background color of the title and then moving on to the actual color for the title, using the original gray as the default color. Moving into index.html, we'll first remove the theme attribute from the element, and then we'll specify some new values for these two colors. We'll add a wonderful orange for the color, and then to make things even look more spectacular, we'll use a nice red background. Then when we refresh the browser, we should see that our new colors have been applied. The final thing we're going to do is allow the user to be able to specify the background color for the entire menu. To do that, once again, we'll just add another custom property, and using the originally-intended color as the default. Double check that everything's working, and then finally in index.html, specify a new value, and there's only one color that's going to make the menu look even better, and that's a nice green. A refresh to see if it's all working, and we can see the worst-looking menu you've ever seen in your life. There is a reason why I've chosen such awful colors for this demonstration, and that is to illustrate a point. That point is that if you give the user this kind of flexibility, they will very easily be able to make your component look ugly, as opposed to the theme approach, which is where we can provide a predefined set of colors which we know work well together.
An Introduction to the CSS @apply Rule
CSS custom properties are a great way to give flexibility to component users, but you might have noticed in the last demonstration that the number of custom properties in the title element were starting to grow. This means that a user has to specify many individual custom properties. If we wanted to give users complete control over the title element, we'd have to add even more CSS custom properties, and even then, they wouldn't be able to change the properties we hadn't defined. They couldn't, for example, just set the height. The CSS @apply rule introduces the concept of CSS mixins that allow component authors to pass even more control to users. The caveat to this feature, though, is that at the time of creating this video course, the CSS @apply feature is only available in Chrome behind a flag. There is a polyfill for this feature, but unfortunately, to get this to work, you'll probably need to structurally change how you've written your components. Before we move on to a demonstration of the CSS @apply feature, I'm first going to show you how to enable this flag in Chrome so you can try it out for yourself. To enable a flag in Chrome, just open up the browser and navigate to chrome://flags. Here, you'll be presented with a series of flags you can enable and disable. The flag we want to enable to allow us to use the CSS @apply feature is the experimental web platform features. Choose Enable, and then choose to relaunch the browser. Once relaunched, we'll be able to see that this flag has now been enabled. Now you've enabled the feature, this short demonstration will show you how to use a CSS mixin within the menu component, and demonstrate how a user can modify multiple CSS properties with this feature. So the first thing we're going to do is get rid of that awful green and also remove the other title-specific custom properties. And then we can see, other than the width, we've got the default set of colors and styles. Then if we move into the component itself, the first thing we're going to do in here is we're going to remove all of the custom properties and stick with the default values. Again, just a quick check to make sure that everything's working as planned, and it is. Now we're going to say that we can apply a CSS mixin if one has been provided, and we simply use @apply and then specify the name, just like we would a custom property. Then, inside index.html, just as we would specify a custom property, we can specify the mixin, but this time it's an object as opposed to a single value, and in here we can provide a variety of property and values. We'll start with the background color, specifying a dark gray. Next, we'll specify the color, and we'll set it to a lighter gray. And finally, we'll change the font size, and we'll set that to 2em. In the browser, we can see that each of these properties has been applied, and we've not had to specify multiple individual custom properties to do so. Instead, we provide a mixin which allow us to choose which CSS properties we want to modify instead of being constrained by the custom properties the component author has added to their component. That concludes this module, which has been all about styling web components. In this module, you've been given an introduction to styling web components, which allows users to customize the look and feel of your components to fit with their application theme. You've learnt about CSS custom properties and how to use them in your components, which included understanding which browsers support them. We've created a styling API for both the star rating web component and the slide out menu component, and you were given a brief introduction to the capabilities of the CSS @apply feature.
Production Ready Web Components
Browser Support for the Web Component Specifications
Transpiling the Star Rating Web Component
In this demo, we're going to go through that process. We're first going to use NPM to install Babel, and then we're going to use Babel to convert the ES2015 code in our Star Rating web component to ES5, ready for browsers such as IE11. This demonstration assumes you already have NPM and Node.js installed on your system. Within the Star Rating Web Component project, we're going to open a terminal window within that directory and run NPM in it. Once we've accepted all of the default options, we will now have a package.json file. Next, back in the terminal, we're going to use NPM to install Babel CLI, which is the CLI tools for Babel, which will do all of the work for us. But we're also need to install a Babel preset, which bundles all of the functionality we need to use to transpile our ES2015 code. While that's installing, we'll create a dist folder, which is where our transpile code will go. Once installed, we need to go back into the package.json file. Here, we can see the devDependencies now listed. So, we don't want to have to type the Babel TLI commands every time we want to transpile. So we'll quickly create an NPM script. Instead of test, we'll name it transpile, and then specify the command we want to run. We want to use Babel with the ES2015 preset we installed, and we want to transpile rw-star-rating.js, and we want to place the output with the same filename within the dist folder. To actually run that script, we now go back to the terminal, chose NPM, run, and the name of our script, which is transpile. Once that script is finished executing, we can go into the dist folder and see the new file has been created with the transpiled ES2015 code. Looking through the file, we can see a lot of similarities, but we can see that all of the ES2015 features have been stripped out, and it's been completely converted to ES5.
An Introduction to the Polyfills
Adding the Polyfills to the Star Rating Web Component Project
Now you're aware of the different polyfills and what they do, I'm now going to show you, step by step, how to use them with a typical web component by adding them to the Star Rating Web Component project. We're first going to install webcomponentsjs into the project via Bower. Then we'll add webcomponents-es5-loader to the project to load the required polyfills, and finally, we'll ensure that the ES5 version of the web component is being used. First off, in index.html, we'll remove the reference to the ES2015 version of the component. Next, in the terminal, we're going to use the CLI tool Bower to install the webcomponentsjs suite of polyfills. Here, I'm specifying a version to install, but you'll want to ensure you have the latest. Just make sure it's the same or later version than the one I'm using here. Once that is completed, we should be able to see all the files under the Bower components directory. We can now just include the Web Components ES5 loader file into the head of index.html. Because the loader detects what features a current browser has and loads the required polyfills asynchronously, we need to change how we load the rw-star-rating js file. We don't want to load it until the Web Component polyfills have been fully loaded. To do this, we can hook into the WebComponentsReady event, which the polyfills will dispatch on window when they finish loading. In here, we can create a script element whose source will be the ES5 version of the component from the dist folder. We'll then append the script element to the head of the document so the file will get downloaded and be ready to use. Now we've got an ES5 of our Star Rating web component, and we've ensured that all the required polyfills are going to get loaded. We're not far off getting this component working in all major browsers, including IE11.
Using the ShadyCSS Polyfill
Released11 Apr 2017