Vue vs Traditional CSS - Beginner's Guide

Published 1/28/2019

This article is part of a series:


For traditional websites there are a multitude of ways we can implement CSS. The most common way is to put the styles in its own CSS file.

There is usually a global CSS file and then one CSS file per page. For smaller pages you will also often find the CSS within the HTML page (usually in the <head> part).

This more than often results in big CSS files, that are very hard to manage or refactor without breaking something while doing so.

Vue

In Vue, each component can have its own styling that is scoped to the component.

<template>
<div class="success-message">this will be green</div>
</template>

<style scoped>
.success-message {
    color: green;
}
</style>

Notice the scoped attribute we pass along the <style> tag. Without this, the CSS would be applied globally. Something we want to avoid. So if I go ahead and create another component with a div that uses the class success-message, that div will not become green.

What we end up in Vue is a bunch of small components with only little to no CSS inside each. Gone are the days of having to organize a large set of global CSS somehow, dealing with conflicting style rules and specificity. Together with a CSS framework (see below) you will end up with easily understable and small CSS!

It also avoids constantly having to switch between JavaScript, HTML and CSS files since everything is in one file.

For the CSS you do write, I still recommend following a methodology like BEM.

Dealing with classes

We often have to add and remove classes from elements to apply specific styles.

Traditional

function handleClick() {
    const messageEl = document.getElementById('message')
    messageEl.classList.add('primary')
}
// ...
// ...
function deleteProject() {
    const messageEl = document.getElementById('message')
    messageEl.classList.remove('primary')
    messageEl.classList.add('danger')
}

As you can see, the classList might get modified at any point in the application and it become hard to track.

Vue

In Vue there are multiple ways:

Using Arrays
<template>
<div :class="['consistent-rule', isPrimary ? 'primary' : '']">message</div>
</template>

<script>
export default {
    data() {
        return {
            isPrimary: true,
        }
    }
}
</script>

<style scoped>
.primary {
    color: #369369;
}
</style>
Using Objects
<template>
<div :class={'consistent-rule': true, 'primary': isPrimary}">message</div>
</template>

<script>
export default {
    data() {
        return {
            isPrimary: true,
        }
    }
}
</script>

<style scoped>
.primary {
    color: #369369;
}
</style>

The convenient thing is that all possible classes are together in one place. Of course you can also extract the class out to a computed field.

Check out my e-book!

Learn to simplify day-to-day code and the balance between over- and under-engineering.

Using SCSS (or other preprocessors) (skipping setup)

Traditional

This usually requires you to create a .scss file that will get compiled down to a .css file in the public or dist directory that you then have to import. You will no longer be able to have the CSS in the same file as the HTML.

Vue

Take a look at this please

<template>
<a class="red--text" href="#">this will be red</a>
</template>

<style scoped lang="scss">
$alert: #c04848;
.red--text {
    color: $alert;
}
</style>

The only change that is necessary is to add lang="scss" to the <style> tag.

Using Frameworks

Let's compare two material design frameworks. MaterializeCSS for Vanilla JavaScript/HTML and Vuetify for VueJs.

MaterializeCSS

CSS frameworks are usually just that, CSS frameworks. So if you want a pagination for example, you will have to take care of how many list items you actually want to display.

<ul class="pagination">
    <li class="disabled"><a href="#!"><i class="material-icons">chevron_left</i></a></li>
    <li class="active"><a href="#!">1</a></li>
    <li class="waves-effect"><a href="#!">2</a></li>
    <li class="waves-effect"><a href="#!">3</a></li>
    <li class="waves-effect"><a href="#!">4</a></li>
    <li class="waves-effect"><a href="#!">5</a></li>
    <li class="waves-effect"><a href="#!">6</a></li>
    <li class="waves-effect"><a href="#!"><i class="material-icons">chevron_right</i></a></li>
</ul>

Now when the data changes you have to add / remove <li> elements which can become very messy.

Vuetify

Vue of course, with the UI being synced with the data, can make your life a lot easier.

<template>
<v-pagination
    v-model="page"
    :length="6"
></v-pagination>
</template>

<script>
export default {
    data() {
        return {
            page: 1,
        }
    }
}
</script>

This also avoids all of these data- attributes for many components.


Vuetify also makes the overall HTML more readable. Let's take a look at the popular cards.

MaterializeCSS

<div class="card blue-grey darken-1">
    <div class="card-content white-text">
        <span class="card-title">Card Title</span>
        <p>I am a very simple card. I am good at containing small bits of information.
        I am convenient because I require little markup to use effectively.</p>
    </div>
    <div class="card-action">
        <a href="#">This is a link</a>
        <a href="#">This is a link</a>
    </div>
</div>

Vuetify

<template>
<v-card class="blue-grey darken-1 white--text">
    <v-card-title>
        <h3 class="headline">Card Title</h3>
        <div>I am a very simple card. I am good at containing small bits of information. 
        I am convenient because I require little markup to use effectively.</div>
    </v-card-title>
    <v-card-actions>
        <v-btn flat color="orange">This is a link</v-btn>
        <v-btn flat color="orange">This is a link</v-btn>
    </v-card-actions>
</v-card>
</template>

I don't know about you, but I find the Vue solution much more expressive. I see right away what kind of element we are dealing with (e.g. <v-card-actions>), rather than having to search for it in the class list.

Conclusion

I have to give it to Vue again for making the code once again more readable, and all of that without any complicated syntax like

styled.a`
  padding: 0.5rem 0;

  ${props => props.primary && css`
    background: white;
    color: palevioletred;
  `}
`

It feels really refreshing having only little to no CSS in your components and therefore having only little CSS to worry about when working on a component!