Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scoped style does not work with multi-root child components #5446

Closed
iliubinskii opened this issue Feb 17, 2022 · 7 comments
Closed

Scoped style does not work with multi-root child components #5446

iliubinskii opened this issue Feb 17, 2022 · 7 comments

Comments

@iliubinskii
Copy link

Version

3.2.31

Reproduction link

sfc.vuejs.org/

Steps to reproduce

See preview here: sfc.vuejs.org/

image

What is expected?

First 3 divs should have blue border

What is actually happening?

Only first div has blue border

@LinusBorg
Copy link
Member

It's by design, in my view.

The scoped data property is only inherited onto single root nodes because the intended use case is to add layout styles for that root node.

That doesn't make too much sense for a multi-root component.l, and could in fact have unintended side effects when the number of root nodes changes during the lifecycle of the child component.

So for multi-root components, the right approach would be to add a wrapper element in the parent for layouting purposes.

Provided that @yyx990803 confirms this I'd say we should make the docs more explicit about this.

@iliubinskii
Copy link
Author

That doesn't make too much sense for a multi-root component

The sense is that multiple root elements may use classes:
image

And I may need to apply styles to these classes.

But I can't achieve this neither with :deep pseudo class nor without it:
image

Adding or not adding v-bind="$attrs" also does not help:
image

Here is an updated example:
sfc.vuejs.org
I have very simple need.
I need to add blue border to each section in a component that needs to be multi-root.
How can I do this?
I think this is very simple task that I should be able to acomplish with flexible framework.

@LinusBorg
Copy link
Member

The sense is that multiple root elements may use classes:

Our perspective is that it's not a good idea to break separation of concerns/responsibilities in this way, so we don't go out of our way to support it.

But I can't achieve this neither with :deep pseudo class nor without it:

Yes, :deep doesn't work either in your specific example because it would need one element at least in the parent wrapping the children:

working with a root element in App.vue

How can I do this?

in this specific scenario where the parent has no elements of its own around Child, and Child has multiple roots:
a) Use a non-scoped style
b) use css modules instead of scoped:

<template>
  <Comp class="$styles.section" style="background: yellow;" />
  &nbsp;
  <Comp2 class="$styles.section" style="background: yellow;"  />
</template>

<style module>
.section {
  border: 2px solid blue;
}
</style>

@Rolanddoda
Copy link

@LinusBorg from the comments is a bit unclear if you answered or understood this issue with multi-root components so I am going to give it a try to ask again because in my view this is a bug.

Component with multi-root nodes cannot be styled from parent with :deep() selector

See the demo on Stackblitz

If we have a HelloWorld.vue component with this code:

<template>
  <div class="hello-world">Hi</div>
  <div class="hello-world-2" v-bind="$attrs">Hey</div>
</template>

Then from the parent component, we can't style it within scoped style with :deep() selector nor with by accessing a class we add in the parent:

<script setup>
import HelloWorld from './components/HelloWorld.vue';
</script>

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <HelloWorld class="hello" />
</template>

<style scoped>
.hello {
  background: blue;
  color: white;
}

:deep(.hello) {
  background: blue;
  color: white;
}

:deep(.hello-world-2) {
  background: blue;
  color: white;
}
</style>

So as you can see above, none of the 3 ways works to style the elements of the child component.
This fails only when mutli-root nodes are used in the child component.

Trying to understand your answers

Maybe there is something that I am missing here but how can this behavior be by design? Since multi-root components are supported, why do we have to wrap child components in order to enable :deep() styling?

@LinusBorg
Copy link
Member

LinusBorg commented Feb 19, 2022

There's a difference between a bug and a limitation of a design/solution/feature, wether that limitation is for technical reasons or intentionally put in place.

:deep()

:deep(.some-class-name) was explicitly designed to produce a selector like: [data-v-3478937439] .some-class-name.

That means, this design requires that the child component that you want to have the deep styles bleed into needs to be wrapped in one element in the parent which can carry the data-v-3478937439 attribute. That's how this feature works. Thats the design.

The fact that you can't use it in the specific way you want it to does not make it a bug. It makes it a feature that has a limitation falling short of your desired behavior. If you can come up with a feature proposal of how we can make scoped deep styles work without the requirement of such a wrapper element, and without it being a breaking change - feel free to open an RFC.

Also, I gave you a working solution which uses module instead of scoped. CSS modules work differently and thus can make it happen, but have different trade-offs.

multiple root nodes.

Back in Vue 2, root nodes were required, and the fact that scoped styles could bleed onto the root node was a double-edged sword:

  1. It allowed for an escape hedge when one needs to layout a component without a wrapper element.
  2. But it also meant that style encapsulation of scoped styles was leaky, and not 100% reliable. You can have class name collisions with this on root elements. People were and are bitten by this quite regularly.

During development and RFC phases of Vue 3, we considered removing this behavior completely because of 2., but left it in for single-root-components for two reasons:

  • Allow for easier migration: We don't force people migrating to re-organize for wrapper-elements in multiple places.
  • Point 1. above does have it's merits, especially when it's about layouting a child in relation to the parent - which also doesn't really affect multi-root components as layouting usually is done on wrapper elements.

Now, was this decision perfect? Certainly not, it has trade-offs. Does this mean it's a bug? Absolutely not. But does this mean that there are edge cases where this design is giving users a hard time? absolutely.

Can we change it without a major release? No.

@Rolanddoda
Copy link

@LinusBorg thank you for the detailed explanation. Make sense to me now why this is a limitation. Have a nice weekend

knime-github pushed a commit to knime/webapps-common that referenced this issue Feb 27, 2023
This is necessary in order to style the button in FunctionButton due to vuejs/core#5446
knime-github pushed a commit to knime/webapps-common that referenced this issue Feb 28, 2023
This is necessary in order to style the button in FunctionButton due to vuejs/core#5446
@joaomelo
Copy link

joaomelo commented Mar 16, 2023

I believe a parent component (P) should be able to consume a child component (C) without knowing if C is a single or multi-root component.

If, in component P, we set some CSS class for C, the styles tied to that class should fall through C no matter the root plurality. In my opinion, That API is consolidated in the VUE design and should not be broken for multi-root components.

That fallthrough behavior is a decision for C to control. If it is a simple single root element, that is already settled and working, in the other case of C being a multi-root component, the v-bind="$attrs" would guide the entire fallthrough behavior, including classes and styles coming from the parent.

That makes de API predictable. Someone read this doc and goes one applying v-bind=$attrs as a way to signal which of the root nodes is the nominal target for fallthroughs.

It is not my intention to trash the framework design. I love VUE and use it daily. I also acknowledge that it is common for technical challenges to limit capabilities. That said, if this behavior is permanent, I think it should be declared as a limitation in here and/or here

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants