3

If you toggle display:flex on a shadow root child it also affects the element outside. (All big browsers behave like this.) Why?

There is a web component with a shadow root:

<web-comp style="display: inline-block;"></web-comp>

Inside the shadow root there is a div with display:flex:

div.style="display:flex; align-items:center; height:50px;"

The complete example:

class demo extends HTMLElement {
  connectedCallback() {
    const shadow = this.attachShadow({mode: 'open'});
    
    const div = document.createElement('div');
    div.innerHTML= "I am in a shadow root!"
    div.style="display:flex;align-items:center;height:50px;background:lightblue"
    shadow.appendChild(div);
  }
}
customElements.define('web-comp', demo);
  <h3>flexbox styles do not respect shadow root border</h3>

   <web-comp style="display: inline-block;"></web-comp>
And I am not.

   <button onclick="document.querySelector('web-comp').shadowRoot.querySelector('div').style.alignItems='baseline'">
   Click to change 'align-items' of div in shadow root.
   </button>
danyball
  • 33
  • 6
  • 1
    Can you convert your JSFiddle to [a StackOverflow snippet](https://meta.stackoverflow.com/questions/269753/feedback-requested-runnable-code-snippets-in-questions-and-answers) Your question will remain on SO, but your JSFiddle might be gone in a years time – Danny '365CSI' Engelman Nov 24 '22 at 09:25

3 Answers3

2

Yes, there is an effect, but normal behaviour for inline-block elements with fixed height,

See the green margins. Its the display-block and fixed DIV height that make you think flex affects the <span> elements. It has nothing to do with those elements being Web Components. You can replace the Web Components with DIVs. It is standard CSS Block behavior

<style>
  web-comp {
    display: inline-block;
    background: lightgreen;
    padding: 1em; /* becomes the "margin" above <span> */
  }
  span { background: pink }
</style>
<h3>Click the light green boxes</h3>
<div style="background:green">
  <web-comp></web-comp>
  <span>span</span>
  <span>span</span>
  <span>span</span>
  <web-comp></web-comp>
  <span>span</span>
  <span>span</span>
</div>

<script>
  customElements.define('web-comp', class extends HTMLElement {
    connectedCallback() {
      this.attachShadow({mode:'open'})
          .append(this.div = document.createElement('div'));
      this.div.style = "display:flex;height:60px";
      this.DIValign("center");
      this.onclick = () => {
        if (this.div.style.alignItems == "center") this.DIValign("baseline");
        else this.DIValign("center");
      };
    }
    DIValign(val) {
      this.div.innerHTML = ` align-items: ${this.div.style.alignItems = val}`;
    }
  })
</script>
Danny '365CSI' Engelman
  • 16,526
  • 2
  • 32
  • 49
  • Can you explain that to me? I don't quite understand your example. – danyball Nov 25 '22 at 10:28
  • See the green margins. Its the ``display-block`` and fixed DIV height that make you think ``flex`` affects the ```` elements. It has nothing to do with those elements being Web Components. You can replace the Web Components with DIVs. It is standard CSS Block behavior – Danny '365CSI' Engelman Nov 25 '22 at 10:38
  • Ok, it has something to do with `inline-block`. I see. And inline-block also respects the paddings around. But what does that have to do with align-items in shadow dom? – danyball Nov 29 '22 at 11:58
  • Nothing. Re-create the above where you replace the Web Component ```` with ``
    ``
    – Danny '365CSI' Engelman Nov 29 '22 at 12:23
  • Thats the point: all styles of a shadow should not have any effects outside. Display `flex` is inside the shadow and all changes should not change something outside or on the host element? – danyball Dec 01 '22 at 16:39
2

I understand your confusion as this actually does look like styles from within the shadow root “bleeding out” their shadow boundaries.

However, all the spec guarantees is that rules declared inside the shadow root don’t apply to elements outside. It does not prevent second-order layout effects to affect the rendering of outside elements.

One trivial example where this happens is when an element inside shadow root changes size:

const hasShadow = document.querySelector('.has-shadow');
const shadow = hasShadow.attachShadow({mode: 'closed'});
const div = document.createElement('div');
while(hasShadow.firstChild) {
div.append(hasShadow.firstChild);
}
shadow.append(div);
document.querySelector('button').addEventListener('click', () => {
div.style.height = `${Math.random()*100+25}px`;
});
.wrapper {
  display: flex;
}
.has-shadow {
background: rebeccapurple;
}
.no-shadow {
background: red;
}
<button>Change shadow size</button>
<div class="wrapper">
  <div class="has-shadow">I can haz shadow</div>
  <div class="no-shadow">I can haz light</div>
</div>

Here, when the element in shadow root changes its height, the element outside also has to change height. This may be obvious because we encounter it all the time that we don’t give it much thought.

What’s happening in your case is very similar, just not with height but with baseline.

Because your shadow-root-containing element is set to inline block, it takes part in a baseline-sharing group, the same one that also contains the text “And I am not.”

Now, why, you may ask, does the baseline-sharing group extend into the inner <div>. Doesn’t that generate a block, since it is set to display: flex, not display: inline-flex? Well, this is true, but, inside an inline-block element, this does not break out of the baseline-sharing group. There is nothing special about the shadow root in this, non-shadow elements behave the same way:

<div style="display: inline-block;">
  <div style="display:flex;align-items:center;height:50px;background:lightblue">
    I am in an inline-block!
  </div>
</div>
And I am not.
Raphael Schweikert
  • 18,244
  • 6
  • 55
  • 75
0

To be honest, I can't agree to the example as its layout doesn't seem proper to me. The text "I am not." is innerHTML-text of the body element, if I'm not misleaded. If one properly puts this text into an element for its own -- as done with the text "I am in a shadow root!" --, lets say by <p style="display:flex;align-items:center;height:50px;background:lightgreen">And I am not.</p>, you won't see the effect described.

I see the point of the question, but I don't see the relevance. It just reminds us to correctly layout our documents as intended in the specifications, also to avoid presumably implementation-dependend side-effects. If one doesn't use a hammer as intended, stuff may get broken, I suppose.

Krokomot
  • 3,208
  • 2
  • 4
  • 20
  • And why do the style of body changes if a style in a shadow root changes? All the styles attached to the shadow should "stay in the shadow". Your example with the p element can not show the effect because is `display:flex`. Use a without styles and you can see the effect again. – danyball Nov 25 '22 at 10:15
  • Hm, ok. Well, Engelman's answer seems the most plausible. For your further interest and if needed -- https://drafts.csswg.org/css-scoping/#host-element-in-tree – Krokomot Nov 29 '22 at 02:41