0

I am interested if there's a way to achieve something like the following:

::slotted(input[type="checkbox"]:disabled) ~ ::slotted(label) {
  cursor: not-allowed;
}

By testing it on some of the examples, it does not work.
Specification does not describe if this should be possible or not. MDN does not cover this case as well.

I do not want to enclose neither input nor label inside the shadow-dom as I do not want to handle and/or duplicate the native behavior of those elements.

P.S. I know that I can do that with javascript (for example, by adding class to slotted label), but I'm looking for a plain css solution.

Full example:

<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super();
      const shadowRoot = this.attachShadow({mode: 'open'});
        shadowRoot.innerHTML = `
            <style>
              ::slotted(input:disabled) ~ ::slotted(label) {
                color: red;
              }
              ::slotted(input:disabled) + ::slotted(label) {
                color: red;
              }
            </style>
            <slot name="inputel"></slot>
            <slot name="inputlabel"></slot>`;
      }
  });
</script>
<my-element>
  <input disabled id="input1" type="text" slot="inputel"/>
  <label for="input1" slot="inputlabel">label</label>
</my-element>
Yuriy Kravets
  • 1,183
  • 2
  • 13
  • 28

1 Answers1

2

The full detailed explanation is at: ::slotted CSS selector for nested children in shadowDOM slot


<my-element>
  <input disabled id="input1" type="text" slot="inputel"/>
  <label for="input1" slot="inputlabel">label</label>
</my-element>

ShadowDOM SLOTs turn off the light

The input and label are in lightDOM,
and remain there invisible when reflected (not moved!) to shadowDOM SLOTs

So you have to style them in lightDOM:

    <style>
      /* style lightDOM!! */
      my-element input:disabled ~ label {
        color: red;
      }
    </style>

<template id="MY-ELEMENT">
  <style>
    ::slotted(input:disabled){
      border:1px dashed red;
    }
  </style>
INPUT <slot name="inputElement"></slot> <slot name="inputLabel"></slot>
</template>
<script>
  customElements.define('my-element', class extends HTMLElement {
    constructor() {
      super().attachShadow({mode: 'open'})
             .append(document.getElementById(this.nodeName).content.cloneNode(true));
      }
  });
</script>
<style>
  /* style lightDOM!! */
  my-element input:disabled ~ label {
    color: red;
    font-weight: bold;
  }
</style>
<my-element>
  <input disabled id="inp1" placeholder="This is disabled" type="text" slot="inputElement"/>
  <label for="inp1" slot="inputLabel">input label 1</label>
</my-element>
<br>
<my-element>
  <input id="inp2" placeholder="Not disabled" type="text" slot="inputElement"/>
  <label for="inp2" slot="inputLabel">input label 2</label>
</my-element>

This use case is a bit contrived, there is nothing gained with my-element, user still has to declare input and label

Maybe create a <input-element type="text" label="A Label"> to create above HTML, you then have the disabled CSS in its shadowDOM (no need for SLOTs)

or (pseudo code)

<template id="INPUT-ELEMENT">
  <style>
    input:disabled~label{
      color:red;
    }
  </style>
  <input id="i"/>
  <label for="i"><slot name="label"></slot></label>
</template>

...
connectedCallback(){
  let inp=this.shadowRoot.querySelector("input");
  inp.type = this.getAttribute("type");
  inp.toggleAttribute( "disabled" , this.hasAttribute("disabled"));
}


<input-element type="text" disabled>
  <span slot="label"><b>Fancy Label<b></slot>
</input-element>

if you want to give the user more control


::slotted came got into WebComponents V1 spec after its predecessor caused performance issues in V0
Thus it only takes simple selectors as parameter, and can only style first-level elements in the slot

So in above example ::slotted can style the <span slot="label">, but not the <b> inside.

Note: SLOTs are LIVE connections, you can re-apply CSS at any time, and content changes are immediately reflected to shadowDOM:

document.querySelector("input-element > b").innerHTML="Better Label!";


More SLOT related answers can be found with StackOverflow Search: Custom Elements SLOTs

Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • thank you for your time, but this is exactly what I try to avoid. I do not see any sense in overriding native elements and/or enclosing native elements inside shadow dom. Styling them on the client-side is an option, but I do not have control over that as this component is exported in a library and the user is supposed to only pass input and label elements, the library should handle the styling. – Yuriy Kravets May 19 '20 at 15:49
  • That 'no control' then includes the placement of input and label tags, your user can place the label before the input.. and then use a ``float:right`` to **display** it after the input.. – Danny '365CSI' Engelman May 19 '20 at 17:58