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

Parent component could not override style of children. #521

Open
JounQin opened this issue Dec 5, 2016 · 5 comments
Open

Parent component could not override style of children. #521

JounQin opened this issue Dec 5, 2016 · 5 comments

Comments

@JounQin
Copy link

JounQin commented Dec 5, 2016

Copied from vuejs/vue#4381 (comment)

Consider two component:

// BaseComp.vue

<template>
  <div :class="$style.base">
    BaseComp.vue
  </div>
</template>
<style module>
  .base{
    color: red;
    background-color: green;
  }
</style>
// Hello.vue

<template>
  <BaseComp :class="$style.hello"/>
</template>
<script>
  import BaseComp from './BaseComp.vue'

  export default {
    name: 'hello',
    components: {
      BaseComp
    }
  }
</script>
<style module>
  .hello {
    color: blue;
    background-color: pink;
  }
</style>

Obviously I want to override the style of .base, but actually it does not work because the style of BaseComp.vue is loaded after Hello.vue……

http://test.1stg.me/

@johnathankent
Copy link

@JounQin There was a similar request using <style scoped>, where data-attributes are added by Vue to the root node of the child (a benefit to allow parent components to control the layout of the child). When <style scoped> is applied to both child and parent, the child root node will now have both parent and child data-attributes applied to it.

So, I don't recommend this, but the simple answer for a quick fix would be to add a data-attr to the child component element inside the parent:

<!-- parent.vue -->
<template>
  <child :class="$style.parent" data-style-from-parent><!-- <<== Added custom data-attr here -->
  </child>
</template>

<style module>
  .parent[data-style-from-parent] { /* styles here will override child using CSS specificity */ }
</style>

@johnathankent
Copy link

johnathankent commented Oct 3, 2017

However, I don't think we need to bloat to Vue or Vue loader to accomplish what you want here.

Problems with manually enforcing style render order on dynamic nesting-order-agnostic objects:

  • Predetermining style injection order might defeat the purpose of reusable and reverse-nestable components. It requires more logic to maintain forced order than to refer to a pattern or guide. River is better than Wall.
  • Inception Overriding. If we do add overrides, we will inevitably need to override those, too. This would create bloat in either logic maintenance, or larger CSS specificity.

To avoid bloating Vue or your project, we can use CSS patterns, and update the docs to be more helpful:

  1. Recommended and preferred: Use CSS for what it does best: specificity and cascade inheritance. Since basic double nested components rarely happen, this is the easiest working solution. In most cases, you would style specificity to your parent component's child element.
  2. Easiest: Don't style the root node. Use child root node as a wrapper to be manipulated by the parent for layout. Expect conflicts on the child's root node if you don't. I think this is easier because it is a clean and clear pattern that everyone can understand. There won't be over-riding problems later.
  3. Hack: Use a non-prop data-attribute on the child. As I mentioned earlier, you could use this,
    but I don't recommend it. Re: Vuejs.org - Non-Prop-Attributes. So yeah, this is probably the answer you want for this specific problem, but if overused or depended upon I hunch it may cause problems down the line.

@trainiac
Copy link

trainiac commented Nov 30, 2017

This is quite an ugly problem. Anytime you add a class to a child component, whether it's on the root of child component or passed in as a prop and used deeper in the child component, you lose component style encapsulation. Let me demonstrate, vue-loader determines a component tree's style order output as follows:

Assume your render tree is

<Root>
  <A>
    <C/>
  </A>
  <B>
    <C/>
  </B>
</Root>

Assuming each component had styles your css classes output order would be:

Root
A
C
B

The css classes for C come after A because that was first place C was encountered. So A has to make more specific CSS rules to override C classes. However B does not because it comes after. Now let's say for some reason you updated your render tree to be

<Root>
  <A/>
  <B>
    <C/>
  </B>
</Root>

Now your css classes order would be

Root
A
B
C

Notice C css classes now appear after B classes. This means simply removing a component in one part of the render tree can switch the css classes output order and potentially break style overrides or lack thereof.

@Jinjiang
Copy link
Member

It seems related with #808 I am just trying to solve it.
Thanks.

@trainiac
Copy link

trainiac commented Dec 1, 2017

@Jinjiang Great. Can you explain how your PR will work differently for the example I provided above?

holmesworcester added a commit to CottageClass/cottageclass-map-vue that referenced this issue Oct 25, 2018
holmesworcester added a commit to CottageClass/cottageclass-map-vue that referenced this issue Oct 25, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants