Creating a Spinner Component with Functional CSS and Vue

Spinners. They're everywhere these days and a neat, visual way to show that something's going on. And they're quite easy to make too, which I'll show you in this tiny piece.

Specifically we'll make this fella using functional CSS classes inside a Vue component:

Ingredients

  1. VueJS. (psst... you don't really need it, but hey).
  2. A dash of Functional CSS classes. Whip 'em up yourself or grab some from Basscss. (In my example it's a mix of both).
  3. This minor, global CSS reset *,*:before,*:after {position: relative;box-sizing: border-box;}

Recipe

The Spinner above is quite simple, consisting of the following:

<template>
  <div :class="[circleClasses]">
    <span :class="[spinnerClasses]">
  </div>
</template>

<script>
  export default {
    computed: {
      circleClasses() {
        return `
        width5 height5 circle
        border border-width4 border-silver
        spin
        `
      },
      spinnerClasses() {
        return `
        absolute top-neg5
        fill-height fill-width circle
        border
        border-right-transparent
        border-bottom-transparent
        border-left-transparent
        border-width4
        border-aqua
        `
      }
    }
  }
</script>

What's going on?

It's basically just two circular shapes where the parent circle is animated to spin around using the .spin class:

.spin {
  animation: rotate 1200ms infinite cubic-bezier(.785, .135, .150, .860);
}

@keyframes rotate {
  to { transform: rotate(360deg); }
}

The parent circle is a simply square made from the width5 and height5 classes1 bent into a circle with the circle class, which adds a border-radius of 50%. It's 4px wide as indicated by the border-width4 class and is made silver-ish in color by the border-silver class.

Inside it we add the spinner element (or what gives the illusion of something spinning around inside of the circle). Again it's just a square made into a circle, but the "trick" here is to make it fill the entire width and height of the parent (fill-width and fill-height), turn all border parts except the top one transparent (which we turn blue-ish with border-aqua) and attach it to the to top of the parent with absolute and top-neg5.

top-neg5 just nudges the spinner-circle negative 5 pixels up. You could probably use something like bottom-5 to achieve the same.

Both collections of classes are handled as Computed Properties in this Spinner.vue instance and added to the template afterwards.

That's all, folks

And there you have it. One, lovely Spinner using functional CSS and Vue.

Note: This guy only comes in one size at the moment, but you could definitely improve this by allowing different sizes. For that I'd recommend using props.


  1. You could make the classes more size specific like "width128" if you want.