The Shadow DOM protects your components from style conflicts. The same protection also makes it hard for users to modify the inner style for their own needs. In this lesson we go over 3 ways to define API for a controlled manipulation of encapsulated styles.
Instructor: [00:00] Let's look at our setup here. We have a web component that renders our template into shadow DOM. Inside its template, there's a style definition of blue and underline text and a div with the class text and the string "In the Shadow." We also have a div outside the web component with the same class and the text "Outside the shadow."
[00:22] Finally, we have a global style that applies red and underline text to a div with the class text. In the browser to the right, we can see the text "In the Shadow" with an encapsulated style as well as the red text of the div outside the web component.
[00:36] Knowing CSS, we might think that adding a very specific selector would enable us to change the style. We add a selector that's specific to our web component and set the color definition to purple. We refresh and see that nothing changed. Our web component's content is encapsulated by the shadow DOM.
[00:56] Let's remove the non-working selector and see three ways of allowing external change of an encapsulated style.
[01:02] We first can add a method, changeStyle. It accepts a styles object. We get a reference to the text div. Now we can iterate over the styles object and replace the element styles inside. We refresh, select our element in the Console. We can see we have the new method on the element itself.
[01:39] Now we can use the method by passing an object with a color property. See? The color has changed. We can do the same with textDecoration and see we had the wanted effect. This can be applied to any valid style property.
[01:58] Web components also enables us to expose declarative APIs on element's attributes. We first need to define the attributes we wish to observe using the static getter observedAttributes. It returns an array of attributes to observe, in our case the color and text-decoration.
[02:19] We can now use the attributeChangedCallback hook that fires when an observed attribute has changed. It accepts the attribute's name, the oldValue, and the newValue. Inside, we can use the changeStyle method with the input.
[02:37] We refresh and go to the Elements inspector. We can now define the color attribute with some value on the element itself and see it has changed. We can do the same for text-decoration and see it has changed as well. Since we hook into the element's attributeChanged detection, we can also use the native setAttribute method. We set the attribute color to purple, and we see it has changed.
[03:17] So far, we've used JavaScript in order to allow styles changes inside the shadow DOM. The next way to allow that is via CSS using CSS variables.
[03:30] Inside our component's template, we use the CSS var function instead of the actual value. We pass the variable's name. We need to define the variable in the global style. In order for the variable to apply to the web component, we need to define it under the component selector. We just set the value we would like to apply.
[03:56] When we refresh the page, we see the color is now purple as in the variable defined in the global style. If we remove the variable's definition, we'll see it will default to the browser default, in this case, black text. In order to avoid that, we can define a default color as the second parameter to the var function. We refresh, and we see we have the blue text as default.
[04:21] To summarize, we've seen three ways to allow penetration of our shadow DOM. We've created a method on the element itself that accepts a styles object and applies them to the element. We've also created a declarative API by observing certain element attributes with the observedAttributes static getter and the attributeChangedCallback hook.
[04:41] Finally, we saw how to define CSS variables in the global style and apply them in a component style with the default fallback.