Vue.js Pizza Maker Tutorial (Pairing Pure CSS & Vue.js)

Michael Mangialardi
Coding Artist
Published in
17 min readFeb 10, 2017

What you’re getting into: A fairly lengthy walkthrough of making a Vue.js Pizza Maker. The point of this is to show how to make fun apps with pure CSS images and Vue.js. If you’re into that, you will also dig a video course I released called Power Up with Pure CSS Images & Vue.js to Make Fun Apps.

Live demo: http://codepen.io/mikemang/live/9fb4d84647a0ed6cc52b987624743859

Full code: http://codepen.io/mikemang/pen/9fb4d84647a0ed6cc52b987624743859/?editors=1010

Template to follow along: http://codepen.io/mikemang/pen/c023f33ce4adb923a0fdc870ebdc9c8e

Since National Pizza Day just happened this past week, I decided to make a fun application that makes use of pure CSS images and Vue.js where you get to build a pizza. This app is going to more simple than my previous Vue.js Pokemon Battle. This app, however, will show off some cool things about Vue.js paired with pure CSS that I did not cover in that post, such as class binding, event handler shorthand, and CSS animations.

Let’s start off by breaking down the application and what will need to do.

Breaking It Down

In terms of functionality in the broadest sense, our app will achieve the following:

  1. Allow user to select various toppings on the left-hand side of the screen
  2. If a topping is added, the total cost of the order should be updated
  3. If a topping is added, the topping should also appear on the pizza on the right-hand side
  4. A user will select a topping by clicking a “Yes” or “No” button
  5. After a user inputs “Yes” or “No” for a topping, the next topping option will appear on the left-hand side of the screen

This will be achieved by doing the following:

  1. Create a main rectangular container for the app
  2. Have a left and right container that each take up 50% of the main container
  3. In the right container, add a pure CSS image of a “fully loaded” (all the toppings) pizza and the total cost of the pizza
  4. In the left container, add a pure CSS image for each topping, text for the current topping, and two buttons for “Yes” and “No”
  5. For the left container, add conditional rendering to control which topping image is shown
  6. For the left container, process the input of the user (yes or no) and adjust the total cost and toppings shown on the pizza in the right container accordingly. Additionally, go to the next topping whether input was yes or no
  7. After the user has given input to all the toppings, change the class of the final pizza in the right container so it spins and change the text in the left container to ask the user if they would like to order
  8. Change the text in the left container to say “Out of Order” no matter if they select to order the pizza or not

I completed all of this by doing the following in order:

  1. Created main rectangular container
  2. Added title
  3. Created left and right container
  4. Created “fully loaded” pizza
  5. Created Vue instance
  6. Added v-if data for each topping of “fully loaded” pizza
  7. Added data for prices of each topping, the current price (number), total price (string), and added total price text in right container
  8. Created yes and no buttons and added data for button text
  9. Created CSS image of each topping and added v-if data for each topping as I created them
  10. Wrote function to process user input
  11. Added class binding to add CSS animation for final pizza in right container

Now, let’s start walking through this together.

Note: First, it is recommended you first read my previous post, A Beginner’s Guide to Pure CSS Images. Second, you can either create your own pure CSS “fully loaded pizza” for this now and then continue once complete, or you can following along using the pure CSS “fully loaded pizza” I provided in the template. In either case, we will begin with step 5, creating our Vue instance and filling in the template.

Setup the Right Container

We should currently see what is shown below:

The first thing we will do is create our Vue instance:

JS

var app = new Vue({
el: "#app",
data: {

},
methods: {

}
})

Here, we have a new Vue instance called app. The el of #app means that this Vue instance will be bound to a div with an id of app in our HTML, which we can add like so:

HTML

<div id="app">
<h1 class="title">Vue.js Pizza Maker</h1>
<div class="box">
<div class="left-container">
</div>
<div class="right-container">
<div class="pizza">
//rest of topping divs
</div>
</div>
</div>
</div>

Now, we want to add some data to our Vue instance. The first type of data that we will add will be booleans which we can use to control whether div elements of our “fully loaded” pizza ( <div class = “pizza”> ) will be rendered at the start of the application.

At the start of our application, we only want <div class = “pizza”> to render. Later on, if a user adds a topping on the left-hand side, the boolean will be changed for that topping so it will appear. For example, if a user chooses to add sauce, we will change a boolean controlling the rendering of our <div class=”sauce”> to true so it renders and the sauce appears on the pizza.

This will make more sense as we go along. For now, let’s add the booleans in our data:

JS

data: {
showSauce: false,
showWedgeCheese: false,
showStringCheese: false,
showPepperoni: false

}

Now, we need to associate this data to the HTML elements of our toppings accordingly. We do this by using v-if like so:

HTML

<div class="right-container">
<div class="pizza">
<div v-if="showSauce" class="sauce">
</div>
<div v-if="showWedgeCheese" class="wedge-cheese">
<div class="cheese-1">
</div>
<div class="cheese-2">
</div>
<div class="cheese-3">
</div>
</div>
<div v-if="showStringCheese" class="string-cheese">
</div>
<div v-if="showPepperoni" class="pepperoni">
<div class="pepperoni-1">
</div>
<div class="pepperoni-2">
</div>
<div class="pepperoni-3">
</div>
</div>
</div>
</div>

We now see the following:

Cool, right?

You can change the booleans to true so you can see how the topping appears when you do so. Instead of us manually changing it to true, we will write some code that will change the booleans to true depending on the user input.

We will get to that later. For now, let’s add some data so we can output a total cost which we can change dynamically.

JS

data: {
showSauce: false,
showWedgeCheese: false,
showStringCheese: false,
showPepperoni: false,
currentPrice: 0,
totalCost: "$0"

}

As you can see, we added currentPrice as a number (0) and totalCost as a string ($0). The reason we do this is so we can do mathematical operations to keep track of the current price using currentPrice and then use string concatenation to set totalCost as a combination of the $ + currentPrice.

For now, let’s add some HTML to display text for our totalCost in the rightcontainer:

HTML

<div class="right-container">      <div class="option"><span id="white">{{totalCost}}</span> 
</div>


<!-- our pizza div with all the toppings here -->
</div>

The class and id used for styling are already defined in our template so don’t worry about that.

We injected totalCost in between our div and span by using the syntax:
{{ [vue.js data] }}

Right on!

Our right container is all squared away for now. Let’s set up our left container.

Setup the Left Container

The first thing we will add is some data that will control the text of our current topping option:

JS

data: {
//left container
topping: "Sauce",
//right container
showSauce: false,
showWedgeCheese: false,
showStringCheese: false,
showPepperoni: false,
currentPrice: 0,
totalCost: "$0"
},

This should be straight-forward. We assign the value of “Sauce” to the data called topping which we need to inject into an HTML tag under our left container:

HTML

<div class="left-container">
<div class="option">{{topping}}?</div>
</div>

The only thing to mention is that I added a “?” right after {{topping}} since we are asking if the user would like to add the topping. Also, option is again predefined in the CSS of our template so don’t worry.

We should now see:

Alright, so we have the topping text for our first topping. Now, we need to the pure CSS image for our topping. This, again, is predefined in our CSS. You can just copy and paste the following:

HTML

<div class="left-container">
<div class="option">{{topping}}?</div>
<div class="option-container">
<div class="sauce-can">
<div class="top-rim">
</div>
<div class="bottom-rim">
</div>
<div class="red-label">
</div>
<div class="badge">
</div>
</div>

</div>
</div>

I added a div with a class of option-container which hold our pure CSS image. Then, I added all the divs that make up the sauce container which we now see below:

Lookin’ good!

Next, it’ll probably be easiest to do the following:

  1. Add our Yes and No buttons
  2. Start function to handle user input
  3. Add data to control conditional rendering of our sauce
  4. Add event handlers to Yes and No buttons
  5. Move on to next topping

Let’s knock out number 1 from out list by adding the following predefined buttons to our HTML and the button texts in our Vue instance data:

HTML

<div class="left-container">
<!-- Topping text and topping pure css image stuff here -->
<div class="yes-button">{{yesButton}}</div>
<div class="no-button">{{noButton}}</div>
</div>

JS

data: {
//left container
topping: "Sauce",
yesButton: "Yes",
noButton: "No",

//right container stuff follows
}

We now see our buttons:

Cool!

Now, let’s chip away at writing the logic to handle the user input. To start, let’s add data in our Vue instance so our sauce can pure CSS image can be rendered conditionally:

JS

data: {
//left container
topping: "Sauce",
yesButton: "Yes",
noButton: "No",
showSauceCan: true,
//right container stuff follows
}

We then add the v-if to the parent sauce can div in our HTML so the rendering is controlled by showSauceCan:

JS

<div class="option-container">
<div v-if="showSauceCan" class="sauce-can">
<div class="top-rim">
</div>
<div class="bottom-rim">
</div>
<div class="red-label">
</div>
<div class="badge">
</div>
</div>
</div>

showSauceCan was defined as true in our data because we want it to render at the start of our app since it is the first topping displayed.

Now, we can start writing our function to handle the user input:

JS

methods: {
processInput: function(selection){

}

}

Let’s think about this. This function will be called on the click of either button. We have two buttons and depending which button was clicked will affect what we do. Therefore, we have to specify which button called the function. So, we will pass in a parameter, referenced as selection in our function, which will either be a 1 or a 2. If we pass in a 1, we can know that the Yes button was clicked. If we pass in a 2, we can know the No button was clicked.

So we can add our event handlers to our buttons like so:

HTML

<div @click="processInput(1)" class="yes-button">{{yesButton}}</div>
<div @click="processInput(2)" class="no-button">{{noButton}}</div>

@ is shorthand for v-on. v-on:[event] is how we use event handlers in Vue.js. So our code is saying “If I click on a button, then I will call processInput. I pass 1 as a parameter for our yes button and 2 as a parameter for our no button. Therefore, the function can handle the user input.”

Now, we can start writing the shell of our logic to handle a yes or a no inputted by the user.

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
} else if (selection === 2){
//yes button clicked and do stuff accordingly
}

}

So, what do we want to do if Yes is clicked or No is clicked?

If yes is clicked, we need to set the showSauce to true so the sauce appears on the pizza in the right container.

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
this.showSauce = true
} else if (selection === 2){
//yes button clicked and do stuff accordingly
}
}

Cool!

Now, we also want to update the total cost of the pizza. So, let’s add an array that will hold the prices of our toppings in the order which they will appear.

data: {
prices: [2,2,3,3,3],
//count which option we are on
stage: 1
}

We will need to keep track of what option we are on to know what price to refer to, so I also added some data called stage. Stage is set to 1 since we are on our first option.

We can go back and add more now to our function. First, we want to update the total cost and then, outside of our if/else, increment the stage.

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
this.showSauce = true
this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice

} else if (selection === 2){
//yes button clicked and do stuff accordingly
}

this.stage++
//prep for next option
}

We update the currentPrice (number) to the price of for our current option, which we get by using an index of this.stage-1 (0). We update the string, totalCost, to $ + 2 or $2.

Neat!

We just need to make a quick adjustment, we want the conditional rendering of our left container pure CSS images to be handled for a “Yes” input specific to each stage. So, let’s wrap this.showSauce with an if statement like so:

JS


if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
}
this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//do nothing
}

Now, let’s finish the logic to prep for our next option. After we do this, we will repeat everything we’ve done for each option until all options have been responded to.

First, we will add the next predefined pure CSS image which you can copy and paste:

HTML

<div class="option-container">
<!-- Sauce Can -->
<div class="shredded-cheese-wedge">
<div class="wedge-circle-1"></div>
<div class="wedge-circle-2"></div>
<div class="wedge-circle-3"></div>
</div>
</div>

Next, let’s add data so we can control conditional rendering:

JS

data: {
//left container
topping: "Sauce",
yesButton: "Yes",
noButton: "No",
showSauceCan: true,
showShreddedCheese: false,
}

We set it to false because we don’t want it to be rendered at the start.

Now, let’s toggle showShreddedCheese to true so we can see it after we also toggle off the sauce can. We also want to change output to display “Shredded Cheese”. Note, we will wrap this in an if statement so this only occurs for stage 1.

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
}
this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//yes button clicked and do stuff accordingly
}
//left container pure css image
if(this.stage === 1){
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
}
this.stage++
}

Lastly, we can just add the v-if so the rendering of the shredded cheese topping is controlled by showShreddedCheese.

HTML

<div v-if="showShreddedCheese" class="shredded-cheese-wedge">
<div class="wedge-circle-1"></div>
<div class="wedge-circle-2"></div>
<div class="wedge-circle-3"></div>
</div>

We should see the following if we hit Yes.

And if we hit No…

Cool beans!

Alright, we have now successfully handled our first option. We will repeat everything we just did for all of our other toppings until we are done.

For our shredded cheese, we want to toggle on the topping on the image of our pizza in the right container when Yes is clicked. Therefore, we can just do:

JS

      if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
} else if(this.stage === 2){
this.showStringCheese = true
}

this.currentPrice = this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//do nothing
}

We also write the logic specific to this stage that is not dependent on Yes or No in processInput.

JS

      if(this.stage === 1){ 
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
//next toppings' boolean = true
this.topping = "Mozzarella Cheese"
}
this.stage++

Next, let’s add the boolean data that controls if our next option, Mozzarella Cheese, renders. I will also add showPepperoni while we’re at it which will be needed for our next stage:

data: {
//left container
topping: "Sauce",
yesButton: "Yes",
noButton: "No",
showSauceCan: true,
showShreddedCheese: false,
showMozzarellaCheese: false,
showPepperoniTopping: false,

}

Now, we can add go back and update our code in processInput:

JS

      if(this.stage === 1){ 
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
this.showMozzarellaCheese = true
this.topping = "Mozzarella Cheese"
}
this.stage++

To complete this stage, we just need to add the predefined pure CSS image for Mozzarella and add the v-if . I will also add the pepperoni pure CSS image while we’re at it with the v-if as well:

HTML

<div v-if="showMozzarellaCheese" class="moz-wedge">
</div>
<div v-if="showPepperoniTopping" class="pep-option">
</div>

Alright, if we test this out, we have this much handled:

Cool! Now, we just have to handle the user input for our mozzarella cheese and our pepperoni.

So we repeat what we have done before but now for stage 3:

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
} else if(this.stage === 2){
this.showStringCheese = true
} else if(this.stage === 3){
this.showWedgeCheese = true
}

this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//do nothing
}
if(this.stage === 1){
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
this.showMozzarellaCheese = true
this.topping = "Mozzarella Cheese"
} else if(this.stage === 3){
this.showMozzarellaCheese = false
this.showPepperoniTopping = true
this.topping = "Pepperoni"
}

this.stage++
}
}

Now, we should get…

Neat-o!

Now, let’s do the same for handling the input of the pepperoni option:

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
} else if(this.stage === 2){
this.showStringCheese = true
} else if(this.stage === 3){
this.showWedgeCheese = true
} else if(this.stage === 4){
this.showPepperoni = true
}

this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//do nothing
}
if(this.stage === 1){
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
this.showMozzarellaCheese = true
this.topping = "Mozzarella Cheese"
} else if(this.stage === 3){
this.showMozzarellaCheese = false
this.showPepperoniTopping = true
this.topping = "Pepperoni"
} else if(this.stage === 4){
this.showPepperoniTopping = false
this.topping = "Order"
}

this.stage++
}

We don’t toggle on another pure CSS image in our left container because we have gone through all the options. Therefore, we just update the topping text to just say “Order”.

Sweeet! We are almost done! Now let’s add some final touches!

Class Binding & CSS Animations

The first touch we want to add is having the pizza to start spinning when it’s ready to be ordered.

We actually handle this pretty easily thanks to Vue.js’ class binding. Just as we can control what text we inject with something in our data, we can have data in our Vue instance that controls which class is active on an element.

So…

We will add data in our Vue instance that is set to pizza which is the default class for our pizza in the right container. When the pizza is ready to be ordered, we will manipulate the data so another class is active on our pizza. We will activate a class already contained in the template called spin-pizza, which adds a CSS animation that spins the pizza.

Let’s write this out. First, we can add pizzaClass to our data and set it equal to pizza which is our default pizza class for the pizza in the right container

JS

pizzaClass: "pizza"

Next, we can bind this data to the element we want to control:

HTML

<div class="right-container">
<div class="option"><span id="white">{{totalCost}}</span></div>
<div :class="pizzaClass">

<!-- Topping css classes here -->
</div>
</div>

Last part for this class binding is to set the class equal to spin class in our processInput function:

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
} else if(this.stage === 2){
this.showStringCheese = true
} else if(this.stage === 3){
this.showWedgeCheese = true
} else if(this.stage === 4){
this.showPepperoni = true
}
this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
} else if (selection === 2){
//do nothing
}
if(this.stage === 1){
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
this.showMozzarellaCheese = true
this.topping = "Mozzarella Cheese"
} else if(this.stage === 3){
this.showMozzarellaCheese = false
this.showPepperoniTopping = true
this.topping = "Pepperoni"
} else if(this.stage === 4){
this.showPepperoniTopping = false
this.topping = "Order"
this.pizzaClass = "spin-pizza"
}
this.stage++
}

Here’s the spin-pizza CSS class and keyframe contained in the template:

spin-pizza

.spin-pizza{
animation: spinPizza 1s infinite;
position: absolute;
height: 40%;
width: 45%;
top: 30%;
left: 27.5%;
border-radius: 50%;
background: #FDE4A7;
z-index: 2;
}

@keyframes spinPizza

//animations
@keyframes spinPizza{
0%{
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}

Alright, it’s look pretty cool!

Let’s wrap this up by just changing the text in our left container to “Out of Order” on the input of the user since we don’t want to take an order. We will also remove our buttons via conditional rendering

First, let’s add boolean in data:{} to control the conditional rendering of our buttons.

JS

buttonsOn: true,

Next, let’s add the v-if in our HTML:

HTML

<div v-if="buttonsOn" @click="processInput(1)" class="yes-button">{{yesButton}}</div>
<div v-if="buttonsOn" @click="processInput(2)" class="no-button">{{noButton}}</div>

Finally, we just add the following to our processInput function to handle this last stage:

JS

processInput: function(selection){
if(selection === 1){
//yes button clicked and do stuff accordingly
if(this.stage === 1){
this.showSauce = true
} else if(this.stage === 2){
this.showStringCheese = true
} else if(this.stage === 3){
this.showWedgeCheese = true
} else if(this.stage === 4){
this.showPepperoni = true
}
if(this.stage !== 5){
this.currentPrice += this.prices[this.stage-1]
this.totalCost = "$" + this.currentPrice
}
} else if (selection === 2){
//do nothing
}
if(this.stage === 1){
this.showSauceCan = false
this.showShreddedCheese = true
this.topping = "Shredded Cheese"
} else if(this.stage === 2){
this.showShreddedCheese = false
this.showMozzarellaCheese = true
this.topping = "Mozzarella Cheese"
} else if(this.stage === 3){
this.showMozzarellaCheese = false
this.showPepperoniTopping = true
this.topping = "Pepperoni"
} else if(this.stage === 4){
this.showPepperoniTopping = false
this.topping = "Order"
this.pizzaClass ="spin-pizza"
} else if(this.stage === 5){
this.buttonsOn = false
this.topping = "Out of Order"
}

this.stage++
}

Here, we turn off our buttons and change the text in the left container to “Out of Order”. We also add an if statement so we don’t update the total cost on the click of the Yes button on the previous stage that just says “Order”.

WE ARE DONE!

Live demo: http://codepen.io/mikemang/live/9fb4d84647a0ed6cc52b987624743859

Full code: http://codepen.io/mikemang/pen/9fb4d84647a0ed6cc52b987624743859/?editors=1010

Future Improvements

  1. You can create your own pure CSS images and try to mess around with more animations, transition effects, and/or more pizza toppings (or maybe multiple pizzas).
  2. I will shortly be releasing a new video course called Power Up with Pure CSS Images & Vue.js to Make Fun Apps. This course goes through the basics of Vue.js with the specific focus on making fun, creative applications like in this tutorial. You can reserve your spot by clicking the link I’ve provided.

Any feedback is appreciated!

Cheers,
Mike Mangialardi

--

--