DEV Community

Brandon Zhang
Brandon Zhang

Posted on

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.

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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.

The basics

/* valid rule */
:host {
  display: flex;
}

/* invalid rule */
fancy-button {
  display: flex;
}
Enter fullscreen mode Exit fullscreen mode

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.

: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>
Enter fullscreen mode Exit fullscreen mode
/* hide initially */
:host {
  display: none;
}

/* if it has a 'visible' class, show this button */
:host(.visible) {
  display: block;
}
Enter fullscreen mode Exit fullscreen mode

: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>
Enter fullscreen mode Exit fullscreen mode
:host([theme="round"]) {
  border-radius: 2em;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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 */
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)