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 the 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 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 for complex solutions during the development to solve the arisen 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 attribute do?

Before diving into the technical details, let’s understand what does Vue.js scoped CSS attributes 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 attributes you lower the possibility that the CSS will conflict if you are using different components. Some of the components might be child’s of other components. In this case, the parent component might own CSS rules and the children might also have their own styles.

In simple words, the 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, the element with the attribute dynamically applied, will look like this:

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

Let’s 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 the parent component. One detail you should pay attention to in 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 CSS properties set that make the background of the element look like a pink rectangle. We will see this later. Moving on, let’s create some of the child components that have a BaseShape component as a parent. Here is the component that has a 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 an 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 override 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 you missed the point that the component has a parent with a different style. Or the childs 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), the 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 deep selectors.

As you might see from the previous example, the 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 these little tweaks, we should get this result:

Voilà, it works! Styles were applied to the components we wanted to be applied to. 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 about how you can implement styles for different components so that they won’t override each other. Style of the 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:

  • The situation that was illustrated 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 them work. 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, a class-based strategy for styling is preferred. And the reason for this is because the 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 *