vue-loader icon indicating copy to clipboard operation
vue-loader copied to clipboard

When using scoped CSS, element in slot within transition element doesn't apply child scope

Open RuneInBoots opened this issue 7 years ago • 10 comments

Version

11.0.0

Reproduction link

https://github.com/RuneInBoots/vue-issue

Steps to reproduce

We tried to use scoped CSS in our components. In our application we also make use of slots.

We came across the problem that the elements in the slots were only following the parent scope. We know this because we inspected the element and saw only one data-v-[id], and because our styles weren't applied =]

After searching we found out that our issue lied with the <transition> around our child component.

// childComponent.vue

<template lang="html">
  <!-- <transition> -->
    <section>
      <h1>Child component</h1>
      <h2>text inside child</h2>
      <p>child sets background to gold</p>
      <h2>text from parent with slot</h2>
      <slot name="foobar"></slot>
    </section>
  <!-- </transition> -->
</template>

<style lang="css" scoped>
  p {background-color: gold;}
</style>

// parentComponent.vue

<template lang="html">
  <section>
    <h1>parent component</h1>
    <h2>text inside parent</h2>
    <p>parent sets color to crimson</p>
    <child-component>
      <p slot="foobar"> text from inside parent trough slot</p>
    </child-component>
   </section>
  </section>
</template>

<script>
  import childComponent from 'components/childComponent.vue';

  export default {
    components: {childComponent}
  }
</script>

<style lang="css" scoped>
  p {color: crimson;}
</style>

What is expected?

the text "text from inside parent trough slot" should have the color crimson and background-color gold. It should follow both the parent and the child scope.

What is actually happening?

the text only get the color crimson. it doesn't follow the CSS of the child scope.

RuneInBoots avatar May 18 '17 20:05 RuneInBoots

screen shot 2017-05-18 at 21 14 44 screen shot 2017-05-18 at 21 14 56

RuneInBoots avatar May 19 '17 12:05 RuneInBoots

I have a similar issue

Alexandre-Georges avatar Aug 18 '17 18:08 Alexandre-Georges

@RuneInBoots is absolutely correct that this is how slotted content should behave with scoped CSS. I've also run into this with scoped components and breaks the fundamental way CSS is supposed to work.

The "Cascading" aspect of Cascading Style Sheets dictates that styling should apply to the child of the parent element. When data/elements are slotted into the component that data/elements then become a child of the component itself. Having scoped styling should still respect the "law" of CSS in this regard.

Seemingly, the way around this now seems to be unscoped styles in the component. This then breaks with the idea of componentization. I want the styling in my component to ONLY apply to that component, otherwise I might as well just use global stylesheets (one of the things that the concept of components is supposed to "solve")

All this said, I think this is a pretty big issue and would like to see it resolved.

pezillionaire avatar Aug 18 '17 18:08 pezillionaire

I might have a related issue, although i am not quite sure and can't reliably reproduce it.

In my case when i have a situation like

<template lang="html">
  <wrapper-component>
    <!-- ... -->
    <div v-if="bar" slot="header" class="foo">
      <span class="bar">{{ bar }}</span>
    </div>
    <!-- ... -->
  </wrapper-component>
</template>

And assuming scope data attributes called data-1 for the wrapper-component and data-2 for the component in question:

With scoped styling for .foo, if bar is false at initial component render, .foo does not get the correct scope attribute added when it enters the DOM later, it only gets data-1.

With an added template tag as a workaround like this:

<template lang="html">
  <wrapper-component>
    <!-- ... -->
    <div slot="header" class="foo">
      <template v-if="bar">
        <span class="bar">{{ bar }}</span>
      </template>
    </div>
    <!-- ... -->
  </wrapper-component>
</template>

It works just fine since .foo is in the DOM from the beginning and correctly has data-1 data-2.

Unfortunately i can't seem to reproduce it properly.

  • In synthetic tests on webpackbin .foo and all of its children only ever have data-2.
  • In synthetic tests in our application .foo and all of its children all have data-1 data-2.
  • As mentioned before, in the practical case where the issue arises, .foo has data-1 while all of its children have data-1 data-2. If the v-if condition is true from the start, .foo also has data-1 data-2.

senritsu avatar Sep 21 '17 12:09 senritsu

+1 for similar structure of @senritsu's example. I'm using v-if on default slot. Currently using the template workaround and work, thanks @senritsu!

vue-loader version: 12.2.2 vue version: 2.4.4

DaxChen avatar Sep 29 '17 06:09 DaxChen

Just ran into this issue myself. Currently using the /deep/ scss selector to get around it. If there's a better way to go about this, I'm all ears. :D

mobilehobo avatar Jun 12 '18 14:06 mobilehobo

hehehe yw @mobilehobo

tessaSAC avatar Jun 13 '18 03:06 tessaSAC

Hi guys, I'm from the future 😄 and I have the same issue, but in my case the v-if is on the slot itself within the wrapper-component. Weird thing is that it was working fine for quite some time until now and I haven't even touched a thing related to it (I confirmed it additionally), it just happened after some merges. Another weird thing is that it only happens on initial render, if I go and just re-save the wrapper component to trigger hot-reload, the scope attribute of the parent component is set correctly ¯_(ツ)_/¯ The only way I found to make it work is with use of /deep/ but it doesn't feel right.

StaverDmitry avatar Jul 10 '19 21:07 StaverDmitry

Guys, we're using $vm0.$options._scopeId in context this, for overwrite selectors weight in child slot scope.

image image image

Before:

image

After:

image

Karodar avatar Jul 28 '21 09:07 Karodar

I believe I've hit this same issue today. The scoped styles in the parent's slot stopped working when I removed an unrelated component from the child template. I can't make sense out of it. I did have a v-if in the child's slot. I moved that v-if to a new wrapper div around the child's slot, and it seems to work. This behaviour was very unexpected.

lopis avatar Jul 24 '23 14:07 lopis