<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Claudio Bonfati</title>
    <description>The latest articles on DEV Community by Claudio Bonfati (@claudiobonfati).</description>
    <link>https://dev.to/claudiobonfati</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F337205%2F4e913cfe-a7a3-4367-bf02-b13c83ae614f.png</url>
      <title>DEV Community: Claudio Bonfati</title>
      <link>https://dev.to/claudiobonfati</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/claudiobonfati"/>
    <language>en</language>
    <item>
      <title>The Vaccination Game (Canvas API)</title>
      <dc:creator>Claudio Bonfati</dc:creator>
      <pubDate>Tue, 22 Jun 2021 23:57:57 +0000</pubDate>
      <link>https://dev.to/claudiobonfati/the-vaccination-game-canvas-api-5b9j</link>
      <guid>https://dev.to/claudiobonfati/the-vaccination-game-canvas-api-5b9j</guid>
      <description>&lt;p&gt;Last year I've worked on a project where I had to develop a small game alongside other components. But recently I've decided to isolate this game as a single stand-alone project since it was a good way to share a bit about canvas development and possibly introduce someone that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" rel="noopener noreferrer"&gt;Canvas API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This article is intended to detail the development process of the game and some fundamentals of how to deal with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" rel="noopener noreferrer"&gt;Canvas API&lt;/a&gt; for similar projects.&lt;/p&gt;

&lt;p&gt;First things first, you can take a look at the &lt;a href="https://github.com/claudiobonfati/vaccination-game" rel="noopener noreferrer"&gt;GitHub Project ⭐&lt;/a&gt; or just &lt;a href="https://claudiobonfati.github.io/vaccination-game/" rel="noopener noreferrer"&gt;play the game 🎮&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Before jumping into coding, we must have some things clear in mind, such as the concept of the game, the goal, how to win, and how to lose. If you have played it already, you've probably got it, but here is a quick overview:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;The game consists of a population of 54 people separated in a grid system that keeps them apart from each other. As the game starts, 2 random people are infected by a disease. Infected people tend to infect the nearby population by randomly choosing what neighbors they will try to infect and at what speed the disease will reach them. By clicking at the healthy person we can vaccinate them, allowing them to become immune to the disease. The goal is to trap the disease right at the start, preventing it to spread further through the population and then vaccinate all remaining healthy people. I also added a timer of 30 seconds to make things a little bit more interesting.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start by setting up our Canvas environment. For this project, I've chosed the framework &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;NuxtJS&lt;/a&gt; to work with &lt;a href="https://vuejs.org/" rel="noopener noreferrer"&gt;VueJS&lt;/a&gt; to handle all the interface interactions and also the engine responsible to create the triggers we will be needing later. After this quick introduction, let's start!&lt;/p&gt;

&lt;h1&gt;
  
  
  Global wrapper
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ubjjjpvbbdxykf6yqlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6ubjjjpvbbdxykf6yqlh.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first thing - and the most obvious one - is to "create a NuxtJS project". I set it up with nothing different from a simple project, with an index page, global style, and a single component called "Game" to manage all functionalities and interactions.&lt;/p&gt;

&lt;p&gt;But one thing is important to highlight here. The Game component will handle the &lt;em&gt;interactions&lt;/em&gt; with VueJS, but all &lt;em&gt;behaviors&lt;/em&gt; of the game will be set up directly with Canvas API and a javascript Class we will also be creating later. Remember these two terms, the difference between them will get clear as we talk more about canvas.&lt;/p&gt;

&lt;h1&gt;
  
  
  Markup structure
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpffq6vtriq0283wgnr9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpffq6vtriq0283wgnr9.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All HTML needed was created and styled at the Game component. But again, one thing is an important note here. Our game must have some "sections", which are the steps where the player will be. If we take a look that the player's journey, it starts at a &lt;em&gt;welcome page&lt;/em&gt;, then the &lt;em&gt;game&lt;/em&gt; starts and after the timer goes out (or the player vaccinate all the population), it has two possible endings, they will &lt;em&gt;win&lt;/em&gt;, or &lt;em&gt;lose&lt;/em&gt;. These steps are what we called "sections" here.&lt;/p&gt;

&lt;p&gt;To manage these sections, I chosen the &lt;a href="https://greensock.com/gsap/" rel="noopener noreferrer"&gt;GreenSock&lt;/a&gt; javascript library to handle activating (showing) and deactivating (hiding) each section whenever we need it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Things start to get interesting
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgjm5skgyn4tlzrbezi2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgjm5skgyn4tlzrbezi2.gif" alt="Alt Text" width="1420" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our environment properly set up, we can start talking about the game itself. In a simplistic overview, to work with Canvas we need two main things: a &lt;em&gt;canvas HTML tag&lt;/em&gt; and a &lt;em&gt;javascript function&lt;/em&gt; that will be pointed as our &lt;strong&gt;requestAnimationFrame()&lt;/strong&gt; handler, that will create a 2D context allowing us to draw some shapes onto the canvas. And what does this &lt;strong&gt;requestAnimationFrame()&lt;/strong&gt; function do exactly?!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"&lt;strong&gt;requestAnimationFrame()&lt;/strong&gt; is the method from Web API that tells the browser that you wish to perform an animation and requests that the browser calls a specified function to update an animation [...]"&lt;/em&gt; _ by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now a non-technical explanation: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"It calls a function over and over again for literally every single frame, and this function will draw an updated image onto the canvas with minor differences from the previous frame, simulating the idea of movement."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now let's do this at our Game component. We create a canvas HTML tag and a VueJS method called &lt;strong&gt;animate()&lt;/strong&gt;, and the first thing this method does is to "request-animation-frame" pointing itself as the argument so this function can be called for every frame.&lt;/p&gt;

&lt;p&gt;This is the initial setup for all canvas development. From now on we can start looking at our project specifically, but all the concepts that will be detailed here can be applied to different scenarios.&lt;/p&gt;

&lt;h1&gt;
  
  
  Population grid
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyf4jlwysk419vn721dk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkyf4jlwysk419vn721dk.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you may have noticed, we will need to create a grid to display the population, with lines and columns. At this point, we need to start talking about the basics of what we can draw on a canvas.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;draw()&lt;/strong&gt; function that we talked about earlier receive as the first argument a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Drawing_shapes" rel="noopener noreferrer"&gt;rendering context&lt;/a&gt;, this context is an object with properties and methods that you can use to render graphics inside the canvas element, such as Lines (that can be curved or straight) and Circles. For our game, these are the only two shapes we will be using - since the game is &lt;em&gt;a bunch of lines and circles&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We already have the canvas width and the height values that we used to style it. But now, to create a grid system, we just need to use the power of math to get the starting-point and the ending-point for each line and column. These points are specified as coordinates related to the top side (X-axis) and left side (Y-axis) of the canvas, and that's the part where we start looking at the canvas as a Cartesian Coordinate System that will guide us throughout the next steps.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a person
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j0nk19u3t40w01x2zyq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3j0nk19u3t40w01x2zyq.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is one of the most interesting parts. As we already defined as the game scope, every person has a predefined (and identical) way to behave and interact with each other, but here is a reminder:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"[...] Infected people tend to infect the nearby population by randomly choosing what neighbors they will try to infect and at what speed the disease will reach them. [...]"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When we have a situation like this, the best decision to make is to deal with Classes - where a single structure can have multiple instances.&lt;/p&gt;

&lt;p&gt;Now we can create a javascript class named "Person" in a separated javascript file that will be imported into the Game component. Initially, the class must have at least two things, a &lt;strong&gt;constructor()&lt;/strong&gt;, and a &lt;strong&gt;draw()&lt;/strong&gt; method. The constructor will receive the initial values for each variable the person will need, such as coordinates where it will be placed at the canvas, the current state ("healthy", "infected" or "vaccinated"), what neighbors it will spread the disease when and if infected, the spreading speed, and so on...&lt;/p&gt;

&lt;p&gt;At this &lt;strong&gt;draw()&lt;/strong&gt; method we must create the shapes that will form the face, and this "face" consists of three simple elements (the head, left eye, right eye, and mouth). And since we received the center coordinates for the person at the &lt;strong&gt;constructor()&lt;/strong&gt;, we can again use the power of math to draw all three elements and place them related to this center point.&lt;/p&gt;

&lt;p&gt;It's important to detail here that some aspects of the face will vary based on the person's state property, like the color that will be &lt;em&gt;blue&lt;/em&gt; for healthy people, &lt;em&gt;red&lt;/em&gt; for infected people, and &lt;em&gt;yellow&lt;/em&gt; for the vaccinated ones.&lt;/p&gt;

&lt;p&gt;For now, we must also have in mind that all the population will be instantiated from the Game component, once we have a basic structure for it to work. In fact, thats exactly the next step...&lt;/p&gt;

&lt;h1&gt;
  
  
  Populate grid with 54 people
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffphhqgqu4w2hw7vma1b5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffphhqgqu4w2hw7vma1b5.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the Person class has a simple structure, we can instantiate the population at the Game component. This process will take a bit to finish since this is the moment we need to define no only the center coordinates for each person, but also randomly define the directions it's going to infect its neighbors and randomly define the spreading speed for each "disease arm". &lt;/p&gt;

&lt;p&gt;For this logic to be simulated with javascript, we can use &lt;strong&gt;Math.random()&lt;/strong&gt; method to create a random value from 0 to 1 and pass this value to the Person class.&lt;/p&gt;

&lt;p&gt;There is also one thing that must be clear here. If you recall one detail of the game scope, each person will be able to infect their neighbors by trying to infect them once the disease has reached them. The mechanic behind it is simple: "if the disease arm reaches the neighbor, and it's not vaccinated yet, the neighbor will turn into an infected individual". To create this logic, two things will be needed: the first is that at the Person class we will create a function able to try to infect the current person, and the second thing is that for each person of the population we will need to store the instances of its surrounding neighbors so we can trigger this &lt;strong&gt;tryToInfect()&lt;/strong&gt; method once the disease reaches them.&lt;/p&gt;

&lt;h1&gt;
  
  
  Disease spreading
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo8u8r53sxxwdiiokcsl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo8u8r53sxxwdiiokcsl.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mathematics starts to have a bigger role here. If you ever thought that you would never use the Pythagorean Theorem in your life, I'll try to convince you otherwise. Looking to a single person, they can try to infect their neighbors in 8 different directions (&lt;em&gt;top&lt;/em&gt;, &lt;em&gt;top-right&lt;/em&gt;, &lt;em&gt;right&lt;/em&gt;, &lt;em&gt;bottom-right&lt;/em&gt;, &lt;em&gt;bottom&lt;/em&gt;, &lt;em&gt;bottom-left&lt;/em&gt;, &lt;em&gt;left&lt;/em&gt;, &lt;em&gt;top-left&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;This article wont turn into an Algebra class, but if you think about it for long enough you will start to see some triangles being formed to define all the 8 arms of the disease related to the center of the face and two close neighbors. The principle that needs to be mentioned is that for each one of these arms we must have stored all the way-points between the center and neighbor's edge in an array so we can control the arm movement and its speed until it reaches the neighbor and try to infect them. And to accomplish that, there isn't much we can do besides applying some algebra formulas to get and store the values.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deliver the vaccine to the population
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58zaub48tgexwaxz5i2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe58zaub48tgexwaxz5i2.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to create the &lt;em&gt;interaction&lt;/em&gt; that will wait for the player to click/tap at some person, and the &lt;em&gt;behavior&lt;/em&gt; to apply the vaccine that will be triggered with this &lt;em&gt;interaction&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;First I created a method at the Person class called &lt;strong&gt;applyVaccine()&lt;/strong&gt;. The idea behind it is also simple: "if the person is not 'infected', change its state to 'vaccinated'".&lt;/p&gt;

&lt;p&gt;After creating this method we can create the event listener to wait for the player's interaction to trigger the &lt;strong&gt;applyVaccine()&lt;/strong&gt; method. The trigger can be built receiving the coordinates from the mouse position related to the canvas element, and these coordinates must be compared with the existing center point from every person instantiated. And if the difference between these two points is smaller than the radio of the head circle, the player clicked at a person.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sound Effects and Scoreboard
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht8wm7y8d90rz69w7glu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fht8wm7y8d90rz69w7glu.png" alt="Alt Text" width="800" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are getting to the end. Now we reached a point where the "soul of the game" is already created, the main &lt;em&gt;interactions&lt;/em&gt; (witch are the functions we've defined at the Game component) and &lt;em&gt;behaviors&lt;/em&gt; (which are the methods created at the Person class), we can focus some effort at the smaller things, such as the scoreboard, the timer, and sound effect management.&lt;/p&gt;

&lt;p&gt;We stored all instances of the Person class, and with this list we can easily retrieve the current state of each one of them, calculate its percentage, and display it on the Scoreboard. It's always important to remember that for all functions that we want to run for each frame, it must be executed at the &lt;strong&gt;animate()&lt;/strong&gt; method, and with the Scoreboard update, it's no different.&lt;/p&gt;

&lt;p&gt;Sounds effects can be easily implemented using &lt;a href="https://www.npmjs.com/package/howler" rel="noopener noreferrer"&gt;Howler.js&lt;/a&gt;, an awesome library able to manage mp3 files in a reliable way across all platforms. It works in a similar way as &lt;a href="https://greensock.com/gsap/" rel="noopener noreferrer"&gt;GreenSock&lt;/a&gt;, we instantiate the audios, and play/pause/restart them whenever it's needed.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;Working with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" rel="noopener noreferrer"&gt;Canvas API&lt;/a&gt; usually requires more than we initially think, but between all the math, rules, and exceptions we create, the logic of the game can be found as a simple and straightforward storyline, just like it was described at the beginning of this post.&lt;/p&gt;

&lt;p&gt;As it was mentioned before, I created this game in particular as a stand-alone project with the idea to introduce new developers to canvas development. Trying to accomplish that, the &lt;a href="https://github.com/claudiobonfati/vaccination-game" rel="noopener noreferrer"&gt;⭐ GitHub Project ⭐&lt;/a&gt; is full of comments for every function, method, code block, and property that needs guidance describing what it does. With these comments, the whole story I told here can also be found in javascript language right there.&lt;/p&gt;

&lt;p&gt;When looking for the final project I wouldn't say it was easy to develop, there were a ton of problems along the way, crashes, conflicts, things that I initially had no idea how to fix, but as I said at an old article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Start from the basics, recognize how the next step looks like, and work on it. Problems are inevitable and that's what makes each project unique in some way, and winning these small battles is one of the things that motivate us to go to the next one."&lt;/em&gt; _ from &lt;a href="https://dev.to/claudiobonfati/what-if-linkedin-was-beautiful-23go"&gt;What if LinkedIn was beautiful?&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's all, everyone. If you made this far, congratulations, and thank you for reading. And also, feel free to &lt;a href="https://www.linkedin.com/in/claudiobonfati/" rel="noopener noreferrer"&gt;connect with me&lt;/a&gt; on LinkedIn.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>gamedev</category>
      <category>vue</category>
      <category>showdev</category>
    </item>
    <item>
      <title>What if LinkedIn was beautiful? - New UI</title>
      <dc:creator>Claudio Bonfati</dc:creator>
      <pubDate>Fri, 19 Mar 2021 18:37:14 +0000</pubDate>
      <link>https://dev.to/claudiobonfati/what-if-linkedin-was-beautiful-23go</link>
      <guid>https://dev.to/claudiobonfati/what-if-linkedin-was-beautiful-23go</guid>
      <description>&lt;p&gt;Hey everyone, three months ago I started a side-project with the goal to create a beautiful user interface for the web version of our beloved LinkedIn. The idea was to enhance some javascript skills by building a smoothly transitioned and friendly interface that I, personally, would like to use when browsing LinkedIn.  &lt;/p&gt;

&lt;p&gt;First things first, here is the &lt;a href="https://claudiobonfati.github.io/linkedin-redesign-app" rel="noopener noreferrer"&gt;LIVE project&lt;/a&gt; and also the &lt;a href="https://github.com/claudiobonfati/linkedin-redesign-app" rel="noopener noreferrer"&gt;GitHub project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As the goal here wasn't to design an UI, but to develop it using &lt;a href="https://reactjs.org" rel="noopener noreferrer"&gt;React.js&lt;/a&gt;, searching around I found an amazing redesign concept by &lt;a href="https://www.behance.net/gregoirevella" rel="noopener noreferrer"&gt;Gregoire Vella&lt;/a&gt; that was pretty close to what I had in mind. Inspired by his work I coded the interface proposed. Here is a quick video of the final result:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://player.vimeo.com/video/522611975" width="710" height="399"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;I'm writing this article to share a quick overview of the development process, walking through some challenging parts, explaining some decisions I made on the way, some of the troubles and the learnings during the whole process. I'm hoping this article can help someone in some way.&lt;/p&gt;

&lt;h1&gt;
  
  
  The API
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwk298rnmd6qt74q6vol.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffwk298rnmd6qt74q6vol.png" alt="Banner with JSON File as background" width="800" height="226"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Development
&lt;/h4&gt;

&lt;p&gt;Before jumping into the interface, the first task in my To-Do list was to create an API able to serve the data in the structure that I would need them. I didn't need to create it from scratch using a real database since it wouldn't be necessary to insert/update or delete any data — I just needed an API returning all the data that I would use. To achieve that, I used the awesome package &lt;a href="https://github.com/marmelab/json-graphql-server" rel="noopener noreferrer"&gt;json-graphql-server&lt;/a&gt; which does an amazing job creating a GraphQL API with static data that can be stored in a simple JSON file on the server — I just realize that the package name does a pretty good job describing it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Problems?
&lt;/h4&gt;

&lt;p&gt;The only inconvenience I had with the API was that json-graphql-server, unfortunately, could not easily handle a scenario where a collection refers to multiple items from another collection. For instance, a "Recommendation" system, where users can write a recommendation to a friend and also receive a recommendation from someone else. In this case, we would have the &lt;em&gt;author&lt;/em&gt; and the &lt;em&gt;target&lt;/em&gt; fields, both referring to the "Users" collection. This issue could be solved with an intermediate collection acting in the middle. Initially, this collection wouldn't be necessary, but apart from that, everything went well.&lt;/p&gt;

&lt;p&gt;So, static GraphQL API as a back-end, done! To the UI!&lt;/p&gt;

&lt;h1&gt;
  
  
  The Interface
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc19sqi0q7egbcnf3jfw3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc19sqi0q7egbcnf3jfw3.gif" alt="Banner with some widgets" width="600" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Development
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://reactjs.org" rel="noopener noreferrer"&gt;React.js&lt;/a&gt; plays the main role here, using &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; for File-system Routing, as well as SSR, Images Optimisation and a lot &lt;a href="https://nextjs.org/#features" rel="noopener noreferrer"&gt;other advantages&lt;/a&gt; the framework provides us.&lt;/p&gt;

&lt;p&gt;I would say that about 50% of the total time spent on this project was coding just the interface, creating all the display components, styling them with CSS Modules — where SASS could be used — making sure the components would still work on mobile devices, and so on... In the second week, I had to refactor some of the components from Class-based to Function-based components. At a certain point, I realize that &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;React Hooks&lt;/a&gt; could easily handle all scenarios that would be necessary and there wasn't actually any real advantage for them to be created as javascript Classes. And also, Function-based components require less code, which is a plus for everyone.&lt;/p&gt;

&lt;p&gt;With the all components created and working property, it was about time to integrate the app with our static API. In order to do that, &lt;a href="https://www.apollographql.com/docs/react" rel="noopener noreferrer"&gt;Apollo Client&lt;/a&gt; was imported into the project to manage all the GraphQL requests the app would run. With Apollo in place, the pages could be created individually, requesting the data from the API, passing them to the child components, and rendering them. Even though Function-based components were the choice I made for the display components, the pages were kept as Classes to handle some complex scenarios with the infinite scroll functionality and conditional data fetching. No problem so far, just a lot of work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Animations and Interactions
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pramns62j8mul2iegjo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8pramns62j8mul2iegjo.gif" alt="Banner with some animated widgets" width="720" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Development
&lt;/h4&gt;

&lt;p&gt;No need to say that &lt;a href="https://www.npmjs.com/package/gsap" rel="noopener noreferrer"&gt;GreenSock&lt;/a&gt; is one of the most complete animation javascript library we have available today. The framework was used as the main source of animations that come directly from user interactions, like tabs, popups, drop-downs, etc.&lt;/p&gt;

&lt;p&gt;Besides the user interactions, we also need to have smooth navigation between pages, to create a friendly user experience, and that's where &lt;a href="https://www.npmjs.com/package/framer-motion" rel="noopener noreferrer"&gt;Framer Motion API&lt;/a&gt; takes place in our LinkedIn. It has the ability to intermediate between Next router system applying CSS animations while leaving/entering a page. The implementation was quite easy, without any trouble... Except for the fact that it had a serious issue after building the project and testing it live.&lt;/p&gt;

&lt;h4&gt;
  
  
  Problems?
&lt;/h4&gt;

&lt;p&gt;The problem here was that Next was creating a conflict with Motion API for some components down in the component tree from React — when a page was changed, Next core unmounted only the CSS Modules from some components too fast, not giving Motion API enough time for the page to fade-out of the screen, so the CSS was lost before the elements leave the page — the problem only happens after the project is bundled into static files, strangely enough, this behavior doesn't happen on Next development mode.&lt;/p&gt;

&lt;p&gt;In fact, this is still an &lt;a href="https://github.com/vercel/next.js/issues/17464" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; at Next (by the time this post was written). Currently, there are some workarounds available to solve the problem, but they have their downsides as well. Fortunately, the problem drew a lot of attention at GitHub community, and hopefully, it will soon be fixed.&lt;/p&gt;

&lt;h4&gt;
  
  
  Learnings?
&lt;/h4&gt;

&lt;p&gt;Working with animations that takes a big space at the device screen can be tricky sometimes. Targeting the right elements and choosing the right CSS properties is an important task to get a great performance. At first, it may not look a big deal, but it made a huge difference in the performance tests I ran with some old mobile and desktop devices.&lt;/p&gt;

&lt;p&gt;There is also a second topic here. I wouldn't say it was a "problem" but more like a point of attention with the &lt;a href="https://www.npmjs.com/package/framer-motion" rel="noopener noreferrer"&gt;Framer Motion API&lt;/a&gt; integration. As some of the routes are dynamic generated based on data from the server, it is important to handle them with React Memo, where it can prevent components from unnecessary multiple renders. Otherwise, as soon as the router change was triggered — but before the animation — the current page would be re-render, not giving enough time for it to fade-out of the screen (again). These re-renders have a serious impact on the app's final performance, not only affecting the client-side of the application but also increasing the requests to the server leading to possible overloading problems. I would say that the memoization system when used wisely is an awesome optimization technique with a huge performance impact and deserves special attention while developing an application.&lt;/p&gt;

&lt;h1&gt;
  
  
  Data Management
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tz4erp4a5hndlo7ycfi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0tz4erp4a5hndlo7ycfi.gif" alt="Banner with boxes sending data to each other" width="720" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Development
&lt;/h4&gt;

&lt;p&gt;Working with individuals stand-alone display components fit most cases, but there are some scenarios that it's not enough to achieve a goal. The &lt;a href="https://github.com/claudiobonfati/linkedin-redesign-app/tree/master/components/Chat" rel="noopener noreferrer"&gt;Chat component&lt;/a&gt; — which is composed of a master component and three sub-components — is a perfect example of that. The challenge here is not only the interface by itself — with a decent mobile version — but also to make the sub-components communicate with each other in harmony. At first, I thought of using &lt;a href="https://redux.js.org" rel="noopener noreferrer"&gt;Redux.js&lt;/a&gt; for that, but even though it would fulfill the requirements and solve our problem, I've chosen to work with &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;React Context API&lt;/a&gt; instead, that is meant to share data that can be considered “global” for a tree of React components, fitting perfectly to the Chat component case. This wasn't the only place where Context API was required, but as mentioned, this is a "quick overview", so we are going to stick only with the Chat component.&lt;/p&gt;

&lt;h4&gt;
  
  
  Learnings?
&lt;/h4&gt;

&lt;p&gt;React first introduced Context API in version 16 with the goal to solve the issue of props drilling where you avoid passing props through many components in the component tree.  Using Redux, on the other hand, requires not only adding more libraries to the application bundle but also requires following a set of configurations and their boilerplates for the library to be able to manage the application states. Which doesn't mean that Context API replaces Redux and its purpose — to be honest Redux is perfect for larger applications where there are high-frequency state updates — but the point here is to understand the problem and balance the best solution for each case where sharable states are necessary.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fotkdzjcoi4lx263n9i89.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fotkdzjcoi4lx263n9i89.gif" alt="Banner with old cartoon closing" width="760" height="212"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Well, usually the side-projects that I decide to build end up taking me more time than I expected at the beginning, but overall the final result, in this case, was actually better than I expected as well, so I guess it's forgivable.&lt;/p&gt;

&lt;p&gt;The development process should not be a blurry thing, but you don't need to know every single thing you will do beforehand. I divided the development process into individual steps here, only to create a clear structure for the article. These steps are usually merged with each other while developing, and sometimes you'll need to go back a few days and work on something you thought was completely done.&lt;/p&gt;

&lt;p&gt;Start from the basics, recognize how the next step looks like, and work on it. Problems are inevitable and that's what makes each project unique in some way, and winning these small battles is one of the things that motivate us to go to the next one.&lt;/p&gt;

&lt;p&gt;That's all everyone, if you have come this far, thank you for reading. And also, feel free to &lt;a href="https://www.linkedin.com/in/claudiobonfati/" rel="noopener noreferrer"&gt;connect with me&lt;/a&gt; on LinkedIn (this time it's the real one).&lt;/p&gt;

</description>
      <category>sideprojects</category>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Netflix intro animation - Pure CSS</title>
      <dc:creator>Claudio Bonfati</dc:creator>
      <pubDate>Sun, 29 Nov 2020 17:32:31 +0000</pubDate>
      <link>https://dev.to/claudiobonfati/netflix-intro-animation-pure-css-1m0c</link>
      <guid>https://dev.to/claudiobonfati/netflix-intro-animation-pure-css-1m0c</guid>
      <description>&lt;p&gt;Hey everyone,&lt;/p&gt;

&lt;p&gt;Recently I've cloned the Netflix's intro animation using only CSS and I got some great feedback about it, so I thought it would be great to share a bit about the development process of the animation step-by-step.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/claudio_bonfati/embed/mdryxPv?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So here we go!&lt;/p&gt;

&lt;h3&gt;
  
  
  HTML Structure
&lt;/h3&gt;

&lt;p&gt;First I mapped the letters in Netflix's logo, and separated they so I could use it as a base reference for positioning the HTML elements later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdoh6umhnrgl61hfzhrdb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdoh6umhnrgl61hfzhrdb.png" alt="Netflix logo" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the HTML, I've created a container with 4 subdivs named helpers, each helper is designated to be a 'side' of the animated letter, for example, animating the N letter, 3 helpers are going to be used, because the N is divided in 3 individual parts, like this 👉 I\I&lt;/p&gt;

&lt;p&gt;That being said, considering that in the container div we have an attribute named 'letter', I've used the image we export in the previous step, and it placed exactly where the helper divs  should be for each value in the 'letter' attribute, wich supports all letters from the 'NETFLIX' name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn3xnwm7xggknttgpenr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fn3xnwm7xggknttgpenr7.png" alt="Separated letters from Netflix's logo" width="800" height="189"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Animation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw54thypsvulodi8xwwg1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw54thypsvulodi8xwwg1.gif" alt="Netflix intro" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it's time to focus on the animation part. The animation is composed by three main steps, the first one is when the letter is fading each one of its sides, until it came to the second part, that is mainly composed by lots of vertical 'lights' with different widths and colors that emerge from the last side of the letter while it's also fading away. The third part is the camera movement applied while the lights are going away - but that is the easiest part.&lt;/p&gt;

&lt;h4&gt;
  
  
  The first part (fading away)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fux1uwhy16knpktdvgbp3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fux1uwhy16knpktdvgbp3.png" alt="Fading away" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fading effect has a clear similarity to a paintbrush "unpainting" the letter. In order to clone this effect, we must acknowledge that a paintbrush doesn't have an uniform tip, some brush tips are made of nylon and polyester. &lt;br&gt;
To do this, we have to build an 'irregular' gradient effect. Using photoshop I've made this brush effect:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq71epfffixl4yeholesc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fq71epfffixl4yeholesc.png" alt="Paintbrush gradient effect" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Later I've created each little piece of the gradient into HTML helpers elements, this pieces was named as a 'fur', each one being a single 'span' tag. And, as it can be seen in the image, each fur has a different gradient effect, that was also replicated in the CSS file.&lt;/p&gt;

&lt;p&gt;With this irregular gradient created, I made they move using simple CSS key-frame animation, each helper having its own delay setting to start one after another.&lt;/p&gt;

&lt;h4&gt;
  
  
  The second part (colorful lights)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1umwm603seyjp6ji0u5f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F1umwm603seyjp6ji0u5f.png" alt="Colorful lights from Netflix animation" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea behind the lights is similar to the paintbrush. But, instead of each little piece being a fur with a gradiente background, each light has a random solid color as background and a box shadow for the illumination effect.&lt;/p&gt;

&lt;p&gt;In order to make the lights "avoid the camera" while its getting closer, it was created a random movement with key-frame animation that make them move sideways, opening an empty gap in the middle of the lights.&lt;/p&gt;

&lt;h4&gt;
  
  
  The third part (camera movement)
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fro21uthbu4ky43l6ck2m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fro21uthbu4ky43l6ck2m.png" alt="Camera movement" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The camera movement was replicated also using key-frame animation, but this time the css property manipulated was the scale of the main HTML container, so it looks like the camera is zooming in the letter while all the other animations are playing at the same time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;The hardest part was to conciliate all animations to be played at exactly the time they should be, so 'animation-delay' plays a big role in these scenarios. The approach I toke in the animations was to make sure that the animation could be played with a different background color and even with a different letter color, just changing the SASS variable and all the animation would still work as it should. &lt;br&gt;
The code can still be optimised in many ways, so feel free to play with it if you like.&lt;/p&gt;

&lt;p&gt;Thats all guys. I hope this post can help someone in some way. =)&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>showdev</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
