Introduction & Project Setup
Hey folks, welcome to another Vaadin Tips video. This week I'm going to answer a community member question on how can we style components inside of other components. Now this is a good question because it exposes us to a couple of new concepts that are specific to working with web components that you might not be familiar with if you're coming from developing with just plain CSS or another framework. So let's jump into code and see how we can get this done.
So here I have an app that I created with start.vaadin.com. It has two views, view one and view two. And if we look at the code here, you'll see that the first view has a paragraph of text, it has a child component, has a text field, and a div with a warning class. And if we look at the second view, you'll see it only has a div with some content and then a warning div as well. The child component has a paragraph of text and a button. So pretty basic stuff.
The Shadow DOM Encapsulation Problem
Okay so let's begin by looking at the first view here. You can see that we have an element here that has the tag name one-view. We have styles here in a static getter. I personally prefer the slightly shorter syntax here, so I'm going to change this. Of course the syntax is more or less a matter of preference so you can do however you want. But let's go ahead and add some styling here. So I'm gonna style the paragraph tag, give it some color for the text. So let's do color red like this. And if we save now you'll see that the paragraph becomes red here, but the paragraph within the child component remains black.
The reason for that is if we look at the inspector in our browser, we select the element here, you'll see that the child component here has a shadow root, a shadow DOM that's shielding its contents from all of the CSS from above. So this is one of the concepts of working with web components. We're able to encapsulate CSS within components. It has its upsides and its downsides but the biggest thing is that it requires a different way of thinking about things.
So essentially up until now in CSS, if we kind of look at an analogy towards object-oriented programming, every kind of element is essentially an open class where every property is public. Anyone can change anything on a component. Now with shadow DOM what happens instead is that you as a component creator need to define what is the API that I want to expose for users of this component for styling this. So what do I allow people to style in my component? I can make that either kind of, I let them style everything or I can only give them very specific access to small parts of that.
So let's take a look at a couple different options we have for accomplishing this.
Option 1: Turning Off Shadow DOM
So we'll go into the child component here and one option would be to just simply turn off shadow DOM. So we can do that by saying, uh, overriding the createRenderRoot method and returning this, the element itself. So we're instead of, instead of rendering into the shadow root, we're rendering directly into this element. So if we look at the end result here, you'll notice first of all the text is red, so the styles are now applying to it. And you can see here in the element tree that we don't have a shadow root for this component. So essentially now we've gone back to just plain CSS, no encapsulation within this child component.
Now this can be an easy, easy way of approaching things if you're in a project where you're not reusing components in different views, you're not reusing them across projects for instance. But for the sake of this video, let's look at some of the kind of more fine-grained tools that we have at our disposal.
Option 2: Using CSS Custom Properties
So I'll turn back, uh, turn the shadow DOM back on and let's add some style to the paragraph tag here within the child component. So let's give this a color as well. And here we're going to use a CSS custom property or variable. And let's call this my-color like this. And we can give it a default value, so if we don't have another value, we're going to default to blue here. Okay, and we can see now that the paragraph within the child component is blue, whereas the paragraph in the topmost view is red.
So let's go into the view. We can define the variable value in the parent component. In this case, we can define it essentially anywhere above this component in the hierarchy. It will cascade down like any other CSS property, also through shadow roots. So here we'll define my-color is equal to, let's say, red like this. And this should, ideally if this works, make our child component red here. And that's something that we can then use here as well, to kind of be consistent in our component. We can use my-color here as well.
And that shouldn't change anything. Good. That's something actually that Vaadin uses quite a lot in its built-in Lumo theme. So there are a bunch of these built-in variables that we can use to keep our UI consistent with all the components and other things in Vaadin. So we could use like lumo-space-medium for instance, instead of hard-coded pixel values for our padding here. I'll leave a link in the show notes to the documentation where you can see all the defined variables that we have.
Option 3: Exposing Elements with CSS Parts
Okay, so that's one way we can affect the styling of a child component. Now let's take a look at another way we could do this. So imagine we want to expose this button, or a couple of properties on it, for styling. So we could do the same thing. We could do button and then we could expose things like border-radius and then have a variable for that, and then we could have, I don't know, background and and so on, defining variables for each of these.
But you can kind of very quickly see that there are quite a few things that somebody might want to style on this, and it kind of becomes unwieldy to start defining variables for every single thing there. So for that, we can use something called CSS parts. So I'll remove the styles that we have on the button and instead I'll define a part name on the button here. So we'll call it button.
And then we can go here and define some styles for that part. We do that by first creating a selector for the component itself. So in our case it's the child-component, and then we use double colon, part, and the part name. Our part name is button. And in here we can then start giving it some style. So essentially we can style that button directly now. So we can say like border-radius is equal to say 5 pixels, background is hot pink, padding let's say 5 pixels too, and border none. Well, something like that. And if we save this right now, you'll see that our button is now styled and we were able to pass in a whole bunch of different options in one go. So this is a little bit more convenient if we want to expose larger parts of a component for styling.
This is something that Vaadin components use as well. So if we look at a Vaadin text field for instance, you can see that the different parts here have part names. So we could go ahead and just look how we could style for instance the label of a Vaadin text field. So we can again do vaadin-text-field selector here and select the label part, and we could, let's see, make the font size 20 pixels. Let's just make it really big. And you can see that affects the label here. I will add a link to the documentation on more information about styling specific parts, especially if you need to support older browsers that don't have built-in support for the CSS part spec.
Creating and Using Shared Stylesheets
Okay, now let's take a look at how we can share styles between views and components. So if in our case we have a warning class on a div here, we'd like to have that same warning on both of our views without having to copy-paste CSS between them and having duplicate code.
So let's define the the styles first. So we'll define the selector warning, and let's do the background yellow, a big dark border, let's do four-pixel solid black, and add some padding, 20 pixels, lots of padding, and margin, 40 pixels zero, so separate it from all the other content a little bit. All right, so now we can very clearly see that this is highlighted here.
Now if we want to have it here on the second view as well without having to copy-paste this, it would be handy for us to have a shared style sheet that we can load in both. And that's something we can do. So in the frontend folder here let's go ahead and create a new file, call this shared.css, so plain CSS file. And I will go into the view here and I'll copy over the CSS that we had and I'll put it in here. So what we need to do then is load this CSS file and add it to our styles here. Let's start by loading it. We can use an import for that. I'll call this sharedStyles and we'll import those from two levels up, shared.css like this. And here in our styles right now we're only passing in one CSS block, but this also supports an array of CSS. So in that case what we'll do is we'll first pass in the shared styles and then our view specific styles like this.
And let's go back to our first view here and you can see that that's still visible there. And let's go ahead and copy this same import into our second component, into the second view here. So I'll import the same style sheet here. I'll turn this into an array and I'll add the shared styles here like so. And now you can see the other warning div got styled as well.
Summary of Styling Techniques
All right, so there you have it. A couple different ways you can style components within other components. First one being turning off the shadow root altogether. Just be mindful that if you do, you'll also get rid of some of the features that you get with shadow root. So you don't have any more encapsulation, you're not going to be able to put in child components into slots, and so on.
If you want to have more fine-grained control, you can use CSS properties to define some variables that you take in and use for styling the component. Or you can expose CSS parts for styling bigger chunks of that component. We also took a look at how we can share styles between different components using a shared style sheet. So I hope this video was helpful for you. We have new videos coming out every week so be sure to subscribe and I'll see you in the next video. Thanks for watching.