Styling with CSS classes in Vue.js

Published on March 13, 2017 by

Now that we have seen how to add inline styles by binding to the style attribute, let’s now see how to use classes for styling. While it is convenient and sometimes necessary to use the style attribute, it is considered best practice to write CSS in external stylesheets and apply styles to elements based on classes and element IDs.

I have already prepared some simple CSS classes in advance, which you can see on the right side. Suppose that we have an array of shapes, which can be either a circle or a square in this example, and that each shape has some common styles associated with it. We distinguish circles from squares by having an isRound property on each shape object. With this, I can make a div element which iterates through the shapes array by using the v-for directive. I will just write that before we get to determining which classes should be on the div elements.

<div class="shape"
        v-for="shape in shapes">
         
</div>

Notice that we will always add the shape class.

Similarly to how we bound an object to the style attribute in the previously lecture, we will bind an object to the class attribute now.

<div class="shape"
       v-for="shape in shapes" 
       v-bind:class="{ }">
         
</div>

Now the syntax for this object is slightly different in the context of the class attribute. The keys of the object should match the class names, and the values should be an expression that evaluates to a boolean. If the boolean is true, then the class is added, and otherwise it will not be added to the DOM. Therefore you can add classes to the object even if you don’t know if they are going to be added at runtime – something that we are going to do now.

The first class that we will conditionally add is circle, so I will enter that as the first key within the object. As for the expression, I am going to use the alias for the current iteration of the loop, which is shape. If the isRound property is true on this object, then I want to add the circle class.

<div class="shape"
       v-for="shape in shapes" 
       v-bind:class="{ circle: shape.isRound }">
         
</div>

So if the isRound property on the shape object is true, then the circle class is added. If it is false, then the class will not be added to the DOM.

Now we have checked if the shape is a circle, but we also need to check whether or not it is a square and if so, add the appropriate class. So let’s do the same for the square class, except that we need to negate the expression.

<div class="shape"
       v-for="shape in shapes" 
       v-bind:class="{ circle: shape.isRound, square: !shape.isRound }">
         
</div>

With this, each div element will contain the shape class plus either circle or square.

As we can tell based on the styling, the first shape is indeed a circle, which matches with what is defined in the shape object. And sure enough, the second shape is a square, so everything looks good.

Obviously this code doesn’t go so well with more than two shapes in terms of the isRound property, so let’s add a third shape to the mix; a triangle.

I have already added the CSS styling, so we only need to assign two classes to a div to turn it into a triangle; the triangle class and a class specifying the direction of the triangle, which can be either up, down, left, or right. Well, now we can clearly see that the isRound property was not the best idea in the world, so let’s get rid of it and add a more generic property which holds the type of shape. I will name it shape.

shapes: [
    { shape: 'circle' },
    { shape: 'square' }
]

We can now make the assumption that the value of the shape property will match a CSS class for a given shape. So how do we apply this class to the div element? When we supply an object as the binding to the class attribute, we specify the class name as object keys, but we need something more dynamic in this case. We can solve this by using an alternative syntax which lets us specify an array instead of an object. This allows us to specify an array of expressions, where each expression can be a string or the name of a data property, for instance. So let’s clear out what we already had and start over and use the array syntax.

<div class="shape"
       v-for="shape in shapes" 
       v-bind:class="[  ]">
         
</div>

Adding the appropriate class now is as easy as typing the name of the property containing the class, in this case the shape property on the shape alias.

<div class="shape"
       v-for="shape in shapes" 
       v-bind:class="[ shape.shape ]">
         
</div>

Running the code now will reveal that we got the circle and square shapes working again, this time in a more dynamic fashion. So far so good. Let’s now work on the triangles, so we need to add some objects to the shapes array. The class name for the triangle shape is not so surprisingly triangle, and besides that we need to specify a direction, which can be either up, down, left, or right. I will just add a triangle for each direction.

shapes: [
	{ shape: 'circle' },
	{ shape: 'square' },
	{ shape: 'triangle', direction: 'up' },
	{ shape: 'triangle', direction: 'right' },
	{ shape: 'triangle', direction: 'down' },
	{ shape: 'triangle', direction: 'left' }
]

For the triangles to show up, we need to add the direction as a class too, as I have added styles for the different directions within the stylesheet. But wait, not all shapes have a direction, so how can we add classes dynamically? Remember that the array syntax allows us to add expressions, so we can add a shorthand if statement to check whether or not there is a direction property for the current shape. So if the direction property contains a “truthy” value (i.e. not undefined), we will add the value of the property as a class, and otherwise we will add an empty string, which will obviously not do anything.

<div class="shape"
        v-for="shape in shapes" 
        v-bind:class="[ shape.shape, shape.direction ? shape.direction : ‘’ ]">
         
</div>

Now we should see the triangles appear, so let’s try again. And sure enough, now we see four amazing looking triangles. But let’s take it one step further, because I want to show you something else we can do with the array syntax. Suppose that we want to apply an animation to some of the shapes when we load the page.

I have already added a class named animate which takes care of this. So all we need to do, is to add this class to a shape to make it animate. But first, we need to know which shapes should animate, so I will add a boolean property named animate to a couple of the shapes.

shapes: [
	{ shape: 'circle' },
	{ shape: 'square', animate: true },
	{ shape: 'triangle', direction: 'up' },
	{ shape: 'triangle', direction: 'right', animate: true },
	{ shape: 'triangle', direction: 'down' },
	{ shape: 'triangle', direction: 'left', animate: true }
]

Alright, so now we face a small problem. How do we add the animate class to the shape depending on the truthiness of the animate property? Since we know the name of the class, we could use another shorthand if statement, but I want to show you a cleaner way of doing it. We started out by adding an object as the binding which allowed us to toggle classes based on a boolean value. This would be of use to us right now, so luckily this is also possible with the array syntax. What we can do, is to add an object following the same syntax as we saw earlier, to the array. Since we want to toggle the animate class, we will use this as the object key, and the value will simply be the animate property on the shape alias, which contains a boolean value. If the property does not exist, the evaluation will be false, and the class will not be added.

<div class="shape"
          v-for="shape in shapes" 
          v-bind:class="[ shape.shape, shape.direction ? shape.direction : '', { animate: shape.animate } ]">

</div>

When I run the code now, notice how three of the shapes animate, because the animate class was added to those shapes. As you can see, the array syntax provides a lot of flexibility, as you can mix strings, data properties, objects and more.

Working with objects shares a lot of the same features as we saw in the previous lecture with the style attribute, such as merging classes, referencing computed properties or data properties instead of inline expressions, and more. But since we have already seen this, I don’t want to take up more of your time by showing the same thing again.

Featured

Learn Vue.js today!

Take an online course and become an Vue.js champion!

Here is what you will learn:

  • How to build advanced Vue.js applications (including SPA)
  • How Vue.js works under the hood
  • Communicating with services through HTTP
  • Managing state of large applications with Vuex
  • ... and much more!
Vue.js logo
Author avatar
Bo Andersen

About the Author

I am a back-end web developer with a passion for open source technologies. I have been a PHP developer for many years, and also have experience with Java and Spring Framework. I currently work full time as a lead developer. Apart from that, I also spend time on making online courses, so be sure to check those out!

Leave a Reply

Your e-mail address will not be published.