Vue js scoped CSS attribute

Vue.js scoped CSS: learn how can you benefit from it

One of the CSS attributes that is used and is quite useful, is scoped attribute. This attribute is used and by the Vue developers. Despite the fact that it might seem useless for you at first, it is a true saver in some of the situations. The attribute is especially useful for a applications that are built with a framework like Vue.js that forms an application from different components. So let’s see how we can benefit from the Vue.js scoped CSS attribute.

The applications are becoming more and more complex, so there is a need of complex solutions during the development to solve the arrisen problems. With new problems coming out, new solutions to them are also being created. It is especially easy to mess your style with CSS features that are overriding each other. So in fact, even the CSS itself is being updated. Although these are not the official updates, as there are different new CSS modules that are being developed independently.

What does the Vue.js scoped CSS atribute do?

Before diving into the technical details, lets understand what does Vue.js scoped CSS attribute do. One of the main ways how can you benefit from this is that it allows your child components to have different styles. By applying scoped attribute you lower the possibility that the CSS will conflict if you are using different components. Some of the components might be childs of the another components. In this case the parent component might own CSS rules and the childs might also have their own styles.

In simple words, scoped attribute applies the CSS rules you wrote only specifically for the component. It doesn’t transition to the child components or for any other component. It applies only to this component. Let me demonstrate this. I hope the next example will make it clear for you how does the Vue.js scoped CSS works.

What does the Vue.js scoped css attribute do, is that it adds an additional data-xyz to the selector. So when the page is received by the client browser, element with the attribute dynamically applied, will look like this:

<h1 class="example" data-123>Hello world!</h1>

Lets say we have a parent component that defines a figure and we have a child components (in the folder of Components) that wants to override the style of parent component. One detail you should pay attention is this code, is that the color is set as pink. This is the code for our base shape (let’s name it component as BaseShape.vue):

<template>
  <ul class="list">
    <li class="list-item" v-for="item in items" :key="item">{{ item }}</li>
  </ul>
</template>

<script>
export default {
  props: ["items"]
};
</script>

<style>
.list-item {
  display: block;
  height: 250px;
  width: 200px;
  background-color: #FDD7E4;
  display: inline-block;
  padding: 5px 0;
  margin-top: 2px;
}
</style>

So the parent component has a CSS properties set that makes the background of the element look like a pink rectangle. We will see this later. Moving on, lets create some of the child components that has a BaseShape component as a parent. Here is the component that has blue square as the background. Let’s name it BlueSquare.vue:

<template>
  <BaseShape :items="items" />
</template>

<script>
import BaseShape from "./BaseShape";

export default {
  props: ["items"],
  components: {
    BaseShape
  }
};
</script>

<style>
.list-item {
  height: 250px;
  width: 250px;
  background-color: #0000ff;
  display: inline-block;
  text-align: justify;
  color: white;
}
</style>

And finally, the last component before we will combine everything will be a green circle (we can put the code into GreenCircle.vue file):

<template>
  <BaseShape :items="items" />
</template>

<script>
import BaseShape from "./BaseShape";

export default {
  props: ["items"],
  components: {
    BaseShape
  }
};
</script>

<style>
.list-item {
  height: 250px;
  width: 250px;
  background-color: #00ff00;
  border-radius: 50%;
  display: inline-block;
  text-align: justify;
  color: black;
}

</style>

Now let’s combine everything and let’s check what we will get. Here is the main, App.vue component:

<template>
<div v-cloak id="app">
   <h1>That's the blue square below</h1>
   <BlueSquare :items="['Blue']" />
   <h1>That's the green circle below</h1>
   <GreenCircle :items="['Green']" />
 </div>
</template>

<script>
import BlueSquare from "./components/BlueSquare";
import GreenCircle from "./components/GreenCircle";

export default {
  name: 'app',
  components: {
   BlueSquare,
   GreenCircle
 }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Great, as we have the code, we can check what is the result of this. Let’s run the app to see what there will be displayed:

Both of the figures have the same color and form

Looks like unexpected thing happened. The second component that was defined in the main, App.vue file, is the GreenCircle component. This component had the CSS rules applied that draws a green circle. However this is how the CSS works – it cascades, so the new rules overrides previous ones. And because the BlueSquare was defined before, the style of it was overridden. Let’s switch the lines 3-4 with 5-6 in our App.vue component:

.....
.....
 <h1>That's the green circle below</h1>
 <GreenCircle :items="['Green']" />
 <h1>That's the blue square below</h1>
 <BlueSquare :items="['Blue']" />
.....
.....

Looks like nothing had changed. Now let’s try to add the scoped attribute for the GreenCircle component: <style scoped>

Now both of the shapes are blue squares

Oops. now we are getting two blue squares. Let’s try adding the attribute to the GreenCircle. Maybe it will fix this?

Now both of the components inherited the color of the base shape

Remember I emphasized that we added a pink background color for the BaseShape? So here it is, as the syles of the components are scoped, global style is inherited from the parent. We need to fix this guys! Maybe applying scoped to the BaseShape will solve this?

Now both of the components has no style

Ok, looks like we ruined everything. Now none of the styles is applied. But every problem has a solution, we will try fixing this.

So, if you ever wondered why is your Vue js scoped style not working, that’s probably because your missed the point that the component has a parent with different style. Or the child’s of the same parent has its own styling.

When elements aren’t affected by the scoped attribute use Vue scoped css deep selector

When the content is dynamically generated (was created with v-html), scoped attribute will not be effective. You might add it to the CSS rules, but it won’t make any difference as the global style or the style of another component might be applied. To solve this problem you will need to use the deep selectors.

As you might see from the previous example, scoped selector was effective only when editing the style of the root element – in our case the list element. But how about editing the childs? How can we edit the elements of our list – the different figures?

So basically this is how the deep selector looks like: /deep/. So in your example, let’s add the Vue.js scoped deep selector to the styling of list-item in both objects – BlueSquare and GreenCircle. The code for the GreenCircle.vue file:

/deep/ .list-item {
  height: 250px;
    width: 250px;
    background-color: #00ff00;
    border-radius: 50%;
    display: inline-block;
    text-align: justify;
    color: black;
}

And for the BlueSquare.vue let’s add this:

/deep/ .list-item {
  height: 250px;
  width: 250px;
  background-color: #0000ff;
  display: inline-block;
  text-align: justify;
  color: white;
}

We don’t have to add this to the BaseShape component as we left it as scoped. After this little tweaks, we should get this result:

Voilà, it works! Styles were applied to the components we wanted to be applied. In our case we needed the deep CSS selector, but in the other cases only scoped might solve your problem. As there might different situations, it is important to understand what and where to apply.

When you should and when you shouldn’t use this attribute

And what does this attribute means to you? It means that the Vue.js scoped CSS attribute saves you time and removes a headache. That’s because you don’t need to think how you can implement styles for different components so that they won’t override each other. Style of parent, style of children, styles of a few components that have the same parent. You might quickly get confused. It is definitely a feature you must know and you should know when to use it. So there are some of the situations when you might want to use it:

  • Situation that was iliustrated in our example – you want your child components to have different styles
  • You want to mix global and local styles. With the scoped attribute, here is how you can achieve it:
<style>
/* Style that is global */
</style>

<style scoped>
/* Style that is local and is appliable only for this component */
</style>

Both of it works. In some cases you might need this, especially when you are planning to make additional child objects.

When you shouldn’t use Vue.js scoped attribute:

  • When you are developing a component library. In this case, class-based strategy for styling is preferred. And the reason for this is because scoped attribute creates custom class names that are not very human readable. When you are developing a component library, you want to customize the components, so the overriding can be much more friendly with custom class names. This is explained in the official documentation.

That’s it, I hope I made it clear what is the Vue.js scoped CSS and in which situations you might need this.

Leave a Comment

Your email address will not be published. Required fields are marked *