How to use web components :host selector?
If you’re new to writing vanilla web components, the :host
selector in web components can be a bit tricky and confusing. However, fear not! In this post, I will cover all the different scenarios where you might use the :host
selector, so you can become a pro at using it in your web components.
Please note that if you’re new to web components, this article may not be the best fit for you. Instead, I recommend checking out some web components tutorials on MDN to get started. Once you have a basic understanding of web components, feel free to come back and dive deeper into the topic with this article.
Let’s start by examining a web component like this one shown below.
<fancy-button>
#shadow-root(open)
<button><slot></slot></button>
<just-a-spinner></just-a-spinner>
<style>/* will add css rules here to style this component */</style>
</fancy-button>
The slot
element in this example allows us to use this custom element in our web page like this.
<fancy-button>Click me</fancy-button>
Once the browser has defined this custom element, the text “Click me” will be treated as the textContent
of our slot
element. By the way, the slot
element is display: contents
by default, so you can treat the button
as the parent wrapper of the text “Click me”. I hope that make sense.
Now, let’s move on to the main event: the :host
selector.
1. The basics
/* valid rule */
:host {
display: flex;
}
/* invalid rule */
fancy-button {
display: flex;
}
Inside the shadow root of our custom element fancy-button
, we can’t use the fancy-button
CSS tag selector to target our custom element, as styles inside the shadow root are encapsulated. This is where the :host
selector comes to the rescue.
One thing to note is that a custom element is an inline element by default. However, I’ve found that most of the time, the default inline behavior is not really what we want.
2. :host with class
If we want to target fancy-button
that has a visible
class, we need to add the visible
class inside parentheses, like this. This will ensure that the visible
class is properly applied to the fancy-button
element.
<fancy-button class="visible">Click me</fancy-button>
/* hide initially */
:host {
display: none;
}
/* if it has a 'visible' class, show this button */
:host(.visible) {
display: block;
}
3. :host with attributes
Just like with classes, attributes also need to be enclosed in parentheses when using them as selectors. For example, if we want to select all fancy-button
elements with a theme="round"
attribute.
<fancy-button theme="round">Click me</fancy-button>
:host([theme="round"]) {
border-radius: 2em;
}
In some cases, we may want users to be able to consume our button components with multiple theme attributes.
<fancy-button theme="round large">Click me</fancy-button>
As a component author, we can use the handy ~
CSS operator to target specific theme attributes. This operator selects elements that have the specified attribute with a value that contains a given word, delimited by spaces.
:host([theme~="round"]) {
border-radius: 2em;
}
:host([theme~="large"]) {
font-size: 1.2rem;
}
/* or want to styles button differently */
:host([theme~="round"]) button {
/* styles here */
}
:host([theme~="large"]) button {
/* styles here */
}
4. How to work with :hover :focus :focus-within state?
For element state like :hover
, :focus
and :focus-within
, they also need to be enclosed in parentheses when used as selectors.
:host(:hover) {
background: white;
}
:host([theme="purple"]:hover) {
background: purple;
}
:host(:focus-within) {
border: 1px solid white;
}
:host([theme="purple"]:focus-within) {
border-color: purple;
}
5. What about ::before and ::after suedo-elements?
For ::before
and ::after
suedo-elements, they need to be outside of parentheses when used as selectors.
:host::before {
content: attr(data-content);
}
:host([theme="purple"])::before {
border: 1px solid purple;
}
6. Working with :is selector
We can simply CSS code with :is()
selector to match multiple selectors in the same rule. In this example, the styles are applied to a button
element, but only when its host element has a theme
attribute value of primary
or large
.
:host(:is([theme="primary"], [theme="large"])) button {
/* styles here */
}