Dynamic CSS with Custom Properties (aka CSS Variables) Creating Style Hooks
Transcript from the "Creating Style Hooks" Lesson
>> Since custom properties are inherited properties, they also inherit down shadow DOM boundaries, and they're not affected by any potential old-based resets, like the old CSS property that lets you reset any property in CSS just in one declaration. Custom properties are not affected by that. Users of web components cannot directly style them.
[00:00:25] That's the whole point of encapsulation of web components. But custom properties can provide style hooks for customizing aspects of the component. So let's look at an example. Here, I'm using the Shoelace library of web components. In fact, the website is called Shoelace Style, if you wanna look at them, they're quite beautiful.
[00:00:52] So here I'm using this image-comparer web component. It allows you to specify two images and slide this thing to compare them. And shoelace already supports certain CSS variables for customizing each of their web components. Let's look at the Shoelace website. So, the image comparer is here and you can see there's a CSS custom property section that tells you which custom properties are accepted for styling.
[00:01:30] This is only the beginning of people using custom properties to customize their components, and I'm really glad that Shoelace is doing it. Not all web components libraries have started doing this. So they expose two properties, divider-width and handle-size. So remember in the past when you were using someone's code, if you wanted to customize a part of its styling, you had to drill down and inspect, see how they've implemented things, override rules.
[00:01:57] With custom properties, component authors can just tell you, these are the properties you can set and take care of applying them inside the component. So for example, I can set these and you can see the result. I can set the handle size, and the component just takes care of adapting its style.
[00:02:19] And of course, you can do the same thing in your own components. Even as consumers of components though, even when we're using components that someone else made, we can create our own obstructions through custom properties. So here is the same image-comparer component. And this component also exposes two parts, if we wanna do more heavyweight CSS customization, it exposes the divider in the handler CSS elements that we can style.
[00:02:56] And if we go back to the documentation of Shoelace, we can see right under the Custom Property section, it lists which parts it exposes. In case you haven't seen CSS parts before, they are this API that lets you expose specific elements from inside your web components shadow DOM so that consumers of your component can style them as regular elements.
[00:03:24] So that you don't have to expose your entire structure in light DOM, only those specific elements you want to expose. So here we wanted to make a higher level abstraction so that people can color the entire divider and handle together. Of course we could do that by styling both parts, but it means we need to use these pseudo element selectors and we wanted to make it easier.
[00:03:47] So we could actually have a separate rule, That sets a higher level divider color property. Let's set it to skyblue for now and then just use the property. And now we only need to include this in one part of our CSS and everywhere else where we're using the component, we just use divider color in the main rule that styles the main element.
[00:04:20] And we don't need to worry about which parts are which and which parts we need to style and how, we just set this one property. And obviously you might be thinking, why would we need to have multiple image-comparer components, but this is just an example. The point that this makes applies to any kind of component.
[00:04:42] And of course a similar point applies to native components as well. So here we have a progress bar. And we're using these vendor prefixed pseudo elements to style it because we wanted to make it purple. But if we have many progress elements in our websites, we don't wanna be using vendor prefixed pseudo elements all over the place.
[00:05:09] It's kind of an anti-pattern. We wanna keep them in one place so that when we get a more standardized version of this code, we can just replace it in one fell swoop, not have to hunt it down in multiple places. So for these cases, it also makes sense to define a higher level property.
[00:05:26] Like for example, bar color. Let's make it gold. And then we use it in those pseudo elements and we never have to use the pseudo elements anywhere else. Actually let's make it a bit darker, let's make it orange, slightly darker, yeah it's more of a brown. Anyway let's say we wanted to make it this weird brown.
[00:05:57] And now this means that anywhere where we have a progress element, we can customize it by just using this one property, even in the inline style if we wanted to. So another thing to remember is that custom properties can also help us abstract away ugly parts of our CSS that we don't want to repeat in multiple places.
[00:06:30] So it's good to remember that custom properties inherit down shadow DOM boundaries. This was actually one of the very explicit goals when we designed custom properties. They were designed to afford components to expose styling hooks so that consumers of components can style them without having to break encapsulation.