1

With a normal React/Vue component I normally create a custom CSS class and use that to style the component like so:

<div class="foo">
  ...
</div>

.foo {
}

With web components, there is the additional option to use the :host psuedo-selector. I understand that it can be used to apply styles to the web component HTML element itself.

<div class="foo">
  ...
</div>

.foo {
}
:host {
}

When should I use :host versus a custom CSS class like .foo?

Muhammad Rehan Saeed
  • 35,627
  • 39
  • 202
  • 311

1 Answers1

2

An important part you do not mention, is that React (and other Frameworks), in the build step, rewrites all your classNames, to create "unique" class names.
React does not scope CSS like shadowDOM does.

Below answer is for a Web Component with shadowDOM;
Note that shadowDOM can be attached to regular DOM Elements as well. Same answer applies.

  • :host refers to your ... host...Web Component DOM element: <my-element>
    Bluntly said, you could compare it to html,head,body in global CSS, it is the container element
    The CSS inside does not (have to) know the element name my-element

  • classes (or any CSS selector you know) style Web Component content
    OR, if you are not using shadowDOM, they style your whole document,
    because unlike Frameworks, classNames are not changed, to be "unique", in the Build step.

  • And do learn <slot> behaviour:
    ::slotted CSS selector for nested children in shadowDOM slot

<style>
 .foo {
   border:1px solid red; /* CSS not applied to elements in shadowDOM */
   font: 30px Arial; /* for UX consistancy, font styles DO style shadowDOM */
 }
</style>
<span class="foo">
  <my-element>Hello</my-element>
  Web Components
  <my-element pink>World</my-element>
</span>

<template id="MY-ELEMENT">
  <style>
    :host {
        display:inline-block;
        background:lightgreen;
    }
    :host([pink]) { background:hotpink }
    .foo { font-weight:bold; /* does NOT style anything outside this shadowDOM  */ }
  </style>
  <slot class="foo"></slot>
</template>

<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode: 'open'})
             .append(document.getElementById(this.nodeName).content.cloneNode(true));
    }
  });
</script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • Related to this question, what bothers me here is that I can't find a way to apply a class to the host, instead of using a – Eric Burel Sep 27 '22 at 12:35
  • A Custom Element **is** an HTML Element, so accepts classes just like any other. Notation with class ``foo`` from _inside_ with a `` – Danny '365CSI' Engelman Sep 27 '22 at 12:50
  • In a an usual React component for instance, the root element is part of the render, so you can add classes to it: `() =>
    ...
    `. But a web component adds another level of indirection, so the tree looks like `() =>
    ...
    `. With className based styling you'd want " with "text-blue" applied by the component itself, not the parent (that's the important part here) but I can't find anyway to do that.
    – Eric Burel Sep 27 '22 at 13:48
  • In React the root element IS an HTMLElement, and only those can have classes. A shadowDOM (A construct that does not exist in React at all) is not an HTMLElement thus can not have classes. Comparing React and Web Components is _apples and oranges_. And like I said, if you don't like shadowDOM, then don't use it. – Danny '365CSI' Engelman Sep 27 '22 at 14:50