1

In my HTML, I've got:

<link rel="stylesheet" href="css/styles.css">
<script src="js/components/custom.js"></script> 
...
<custom></custom>

In custom.js, say I've defined:

class Custom extends HTMLElement {
  connectedCallback() {
  const shadow = this.attachShadow({mode: 'open'});
    shadow.innerHTML = `
      <style>
        @import url('/css/styles.css');
      </style>
      ...
    `
  }
}

customElements.define('custom', Custom)

In styles.css, when I define styles, they work. But I can't do something like

custom img {

}

to target an img inside custom. I have to use wrappers like div.custom-contents to target elements within.

Is this a known limitation perhaps with @imports like this, custom elements in general, or am I doing something wrong?

Antrikshy
  • 2,918
  • 4
  • 31
  • 65

1 Answers1

2

It is mandatory for custom elements that the name must consist of at least one letter, followed by a - dash, then at least one other letter. Your web component will not work if you don't conform to that requirement.

Your component uses shadow DOM. No outside styles affect the shadow DOM (other than inheritance), and no styles in the shadow DOM affect elements outside of of your component. This is the main concept behind shadow DOM - components bring their styling, and don't affect other elements on the page they're used in.

Also, you shouldn't attach the shadow DOM in the connectedCallback. That should always be done in the constructor.

You can use the part attribute along with the ::part pseudo element to allow styling from the outside.

connexo
  • 53,704
  • 14
  • 91
  • 128
  • creating a shadowRoot in the ``connectedCallback`` is fine; provided you are aware that callback will execute again when moving the component around in the DOM. – Danny '365CSI' Engelman Jan 24 '22 at 08:45
  • Technically it's fine; but I can hardly think of a reason why you'd not want it in the constructor. I haven't had a case where it would have been desirable to have it in the connectedCallback in 4+ years of developing web components as my main job task. Can you think of a case where constant recreation of the shadow root would be desirable? If not, it just creates the hassle of either cleaning it up in the disconnectedCallback, or having to safeguard the creation of it in the connectedCallback. – connexo Jan 24 '22 at 09:15
  • Just about every time you want to create dynamic content _once_. With or without attributes which you **shouldn't** access in the ``constructor``. Many of my [svg] answers here on SO show using the ``connectedCallback`` only: https://stackoverflow.com/questions/70805170/is-there-a-possibility-to-use-a-dynamic-id-in-svg-fill-url/70807372#70807372 I must be crazy, I've been doing this for 5+ Web Components years. – Danny '365CSI' Engelman Jan 24 '22 at 10:10
  • _"cleaning it up in the disconnectedCallback"_ WHAT needs to be cleaned up? – Danny '365CSI' Engelman Jan 24 '22 at 10:16
  • OP here. I do have a hyphen in my actual component name, forgot to translate that requirement in this sample code. Thanks for the other pointers. Does the element itself count as "outside of your component" though? Like I can style a `

    ` tag using `p { ... }` in CSS. But can't style `` using `custom-element { ... }` in CSS?

    – Antrikshy Jan 24 '22 at 19:42
  • I just checked, adding `custom-element { ... }` right below _or instead of_ the `@import` line also didn't work. I'm trying to apply a `text-align` directly to the element's tag if that matters. Of course I can apply a class or add a child container, but that feels redundant. – Antrikshy Jan 24 '22 at 19:47
  • The custom element itself can be styled from the outside, e.g. you can assign dimension, padding, margin etc. Other than with inherited properties not defined inside your shadow root, you cannot affect styling of elements in the shadow DOM, as my answer explains. – connexo Jan 24 '22 at 21:50