<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Matt Pignatore]]></title><description><![CDATA[UI Developer building SaaS products on the web 🚀 Here to contribute to the community, spread knowledge, and make the internet just a little bit of a better pla]]></description><link>https://blog.mattpignatore.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 13 Oct 2026 09:11:48 GMT</lastBuildDate><atom:link href="https://blog.mattpignatore.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Developing a UI with Vue, Part 5: The Multi-Step Form]]></title><description><![CDATA[In my last post , we developed the Login page by building custom input and button components. Now, we're going to focus on building a multi-step registration form to sign up new users. 
The first time I built one of these I thought it was the coolest...]]></description><link>https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-5-the-multi-step-form</link><guid isPermaLink="true">https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-5-the-multi-step-form</guid><category><![CDATA[Vue.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[UI]]></category><category><![CDATA[app development]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Wed, 18 Aug 2021 00:57:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1629246479947/qTpuNkmZD.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p> <a target="_blank" href="https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-4-slots-inputs-and-buttons">In my last post</a> , we developed the Login page by building custom input and button components. Now, we're going to focus on building a <strong>multi-step registration form</strong> to sign up new users. </p>
<p>The first time I built one of these I thought it was <em>the coolest</em>. I was also intimidated by the process, but after a few attempts and an understanding of what was happening, it became quite fun and simple to pull off. </p>
<h2 id="defining-data-properties">Defining data properties</h2>
<p>We're going to start by defining what fields we'll need to capture, and how many steps the user will have to go through:</p>
<ul>
<li><strong>Step 1</strong> will gather the user's username and email</li>
<li><strong>Step 2</strong> will gather their password and confirmed password</li>
</ul>
<p>In our Vue instance, we'll define those values as data properties. We're also going to add a number property called <code>step</code>, which we'll use to handle which piece of the form gets rendered to the user, as well as a boolean <code>error</code> flag.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// Register.vue</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Register'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">step</span>: <span class="hljs-number">1</span>,
      <span class="hljs-attr">name</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">password</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">confirm_password</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">error</span>: <span class="hljs-literal">false</span>,
    }
  },
}
</code></pre>
<h2 id="using-v-if">Using v-if</h2>
<p>To build the each step of our form, we'll create two <code>&lt;div&gt;</code> elements, each containing the inputs we'll want displayed depending on the step. We can use the <code>v-if</code> directive to provide this conditional logic.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:content</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 1"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Name"</span> 
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your name..."</span>                 
          <span class="hljs-attr">v-model</span>=<span class="hljs-string">"name"</span> 
          <span class="hljs-attr">required</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your email..."</span> 
          <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> 
          <span class="hljs-attr">required</span>
        /&gt;</span>
     <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span>&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
         <span class="hljs-attr">label</span>=<span class="hljs-string">"Password"</span> 
         <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter your password..."</span> 
         <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span> 
         <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> 
         <span class="hljs-attr">required</span>
       /&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
         <span class="hljs-attr">label</span>=<span class="hljs-string">"Confirm password"</span> 
         <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Confirm password..."</span> 
         <span class="hljs-attr">v-model</span>=<span class="hljs-string">"confirmed_password"</span> 
         <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span> 
         <span class="hljs-attr">required</span>
       /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>Notice we're using <code>v-model</code> to bind each value to its property in the <code>data()</code> object, and that we've set up our input component to accept the <code>required</code> prop. That prop is getting passed to the component's native HTML input, which takes some validation work off of our plate!</p>
<p>With that, we've got two steps available to display:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629246837517/bxrXy8GdN.jpeg" alt="vue_5_img2_register@2x.jpg" /></p>
<p><strong>Now we need a way to change the step.</strong> Let's take a look at how to create handlers to get that done.</p>
<h2 id="handling-the-step">Handling the step</h2>
<p>Each button component is set up to receive a <code>@click</code> handler as a prop. Here, we're going to define two handlers inside <code>methods</code>: one to increment the step, and one to decrement it.</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-comment">// ...rest of the object</span>
  <span class="hljs-attr">methods</span>: {
    incrementStep() {
      <span class="hljs-keyword">const</span> formFilled = <span class="hljs-built_in">this</span>.username &amp;&amp; <span class="hljs-built_in">this</span>.email;
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.step === <span class="hljs-number">1</span> &amp;&amp; formFilled) {
        <span class="hljs-built_in">this</span>.step = <span class="hljs-built_in">this</span>.step + <span class="hljs-number">1</span>;
      }
    },
    decrementStep() {
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.step === <span class="hljs-number">2</span>) {
        <span class="hljs-built_in">this</span>.step = <span class="hljs-built_in">this</span>.step - <span class="hljs-number">1</span>;
      }
    },
  }
}
</code></pre>
<p>Each handler is set up with the following logic:</p>
<ol>
<li><code>incrementStep</code> only works when <code>step</code> is equal to 1, so that it can only get as far as 2.  I've also set up logic to ensure that <code>username</code> and <code>email</code> are provided before the user is able to move onto step 2.</li>
<li><code>decrementStep</code> only works when <code>step</code> is equal to 2, so that it can only get as far as 1.</li>
</ol>
<p>When we attach those handlers to the buttons, we'll use some <code>v-if</code> directives too. On step 1,  we want to allow the user to increment:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 1"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"incrementStep"</span>&gt;</span>Continue<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
</code></pre>
<p>And on step 2 the user can either go back, or submit.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span> @<span class="hljs-attr">click</span>=<span class="hljs-string">"decrementStep"</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"secondary"</span>&gt;</span>Back<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Confirm<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
</code></pre>
<p>We'll place the buttons inside of a wrapper div in order to align them to the right with some spacing.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 1"</span>&gt;</span><span class="hljs-comment">&lt;!-- Step 1 inputs --&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span>&gt;</span><span class="hljs-comment">&lt;!-- Step 2 inputs --&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"button-wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> 
      <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 1"</span> 
      @<span class="hljs-attr">click</span>=<span class="hljs-string">"incrementStep"</span>&gt;</span>Continue<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> 
      <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span> 
      @<span class="hljs-attr">click</span>=<span class="hljs-string">"decrementStep"</span> 
      <span class="hljs-attr">variant</span>=<span class="hljs-string">"secondary"</span>&gt;</span>Back<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> 
      <span class="hljs-attr">v-if</span>=<span class="hljs-string">"step === 2"</span> 
      <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>&gt;</span>Confirm<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<p>Now we can we interact with the form:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629246302419/yeLrs73Qo.jpeg" alt="vue_5_img1_register@2x.jpg" /></p>
<h2 id="handling-form-submission">Handling form submission</h2>
<p>To bring it all together, we need to handle the submission of our form. Without a back-end, there's nowhere to really send it. But we can create the handler with some validation to get us started.</p>
<p>Just as we did with the Login page, we are going to add a <code>@submit</code> handler to our form with the <code>prevent</code> event modifier. Then, we'll include the handler as a method in our export object:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"handleRegister"</span>&gt;</span>
    <span class="hljs-comment">&lt;!-- the form we've just built --&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
</code></pre>
<pre><code class="lang-jsx"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">methods</span>: {
      handleRegister() {
        <span class="hljs-keyword">const</span> passwordsFilled = <span class="hljs-built_in">this</span>.password &amp;&amp; <span class="hljs-built_in">this</span>.confirm_password;
        <span class="hljs-keyword">const</span> passswordsMatch = <span class="hljs-built_in">this</span>.password === <span class="hljs-built_in">this</span>.confirm_password;
        <span class="hljs-keyword">if</span> (passwordsFilled &amp;&amp; passswordsMatch) {
          <span class="hljs-built_in">this</span>.error = <span class="hljs-literal">false</span>;
          <span class="hljs-comment">// perform form submission...</span>
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!passswordsMatch) {
          <span class="hljs-built_in">this</span>.error = <span class="hljs-literal">true</span>;
        }
      },
    }
}
</code></pre>
<p>Let's break down the above validation in <code>handleRegister</code>:</p>
<ol>
<li>We're making sure that <code>password</code> and <code>confirm_password</code> have values</li>
<li>We're making sure that <code>password</code> and <code>confirm_password</code> match.</li>
</ol>
<p>If both are truthy, we'll continue on with form submission and clear errors. Otherwise, we set error to true and inform the user of the error, which appears right above the first input inside of the "header" named slot.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Try out our watch list for free today and see what you think.<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"error"</span> <span class="hljs-attr">v-if</span>=<span class="hljs-string">"error"</span>&gt;</span>
    Either a password field is missing, or they do not match.
  <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>Now when there's a problem with the password, the user will see a message like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1629247125102/DRHkHD52o.jpeg" alt="vue_5_img3_register – 1@2x.jpg" /></p>
<p>At this point, we've basically hit a dead end until we get login working and build the actual app.</p>
<p>That's why I'm going to be be turning my attention to the much discussed <strong>Supabase</strong>: the open-source alternative to Google's Firebase that's becoming quite popular. I've always been drawn to UI, so platforms like this are <em>always</em> exciting for me to work with. See you soon  ✌️</p>
<hr />
<h3 id="you-can-find-me-on-twitterhttpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on <a target="_blank" href="https://twitter.com/matt_pigs">Twitter</a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[Developing a UI with Vue, Part 4: Slots, Inputs, and Buttons]]></title><description><![CDATA[In Part 3, we built our Vue app's mobile navigation component. While that was useful, Navigation.vue isn't a component we'll necessarily be reusing anywhere else in our interface. Now, we're going to begin learning how to create dynamic layouts as we...]]></description><link>https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-4-slots-inputs-and-buttons</link><guid isPermaLink="true">https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-4-slots-inputs-and-buttons</guid><category><![CDATA[Vue.js]]></category><category><![CDATA[Sass]]></category><category><![CDATA[UI Design]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Wed, 28 Jul 2021 12:16:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1627468952485/gqyiFxZ0Q.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In <a target="_blank" href="https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-3-mobile-navigation">Part 3</a>, we built our Vue app's mobile navigation component. While that was useful, <code>Navigation.vue</code> isn't a component we'll necessarily be reusing anywhere else in our interface. Now, we're going to begin learning how to create dynamic layouts as well as input and button components that we can reuse anywhere across our UI.</p>
<p>If you're interested in following along or taking a closer look at the code, feel free to check out the app's <a target="_blank" href="https://github.com/mpaitgt/medo-app">Github repo</a>, or you can <a target="_blank" href="https://medo-app.netlify.app/">view the live app</a> that I deployed in just a couple of minutes using Netlify. 😊 </p>
<p><strong>Let's get started...</strong></p>
<h2 id="building-an-input-component">Building an input component</h2>
<p>Many of the components we create for this UI are going to be reused so often that we'll be better off defining them <strong>globally</strong>, rather than meticulously importing them into every parent component we need them in.</p>
<p>Vue 3 actually allows us to do this pretty easily in <code>main.js</code> where the app gets mounted. Let's go ahead and create <code>BaseInput.vue</code> inside of <code>components/global/</code> and add it to our app before the mount. I'd recommend doing this for highly common components like inputs and buttons, but don't go overboard. 🚢</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// globally-defined components 🎉</span>
app.component(<span class="hljs-string">'BaseInput'</span>, BaseInput)

<span class="hljs-comment">// followed by...</span>
app.use(store).use(router).mount(<span class="hljs-string">'#app'</span>)
</code></pre>
<h3 id="vue-template-syntax">Vue template syntax</h3>
<p>The template syntax in Vue allows us to insert dynamic values into our HTML markup. Below, the input component template is being generated with a dynamic <code>label</code> value using mustache tags, or <code>{{ }}</code>.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- BaseInput.vue --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input-wrapper"</span>&gt;</span>
    {{ label }}
    <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
      <span class="hljs-attr">:type</span>=<span class="hljs-string">"type"</span>
      <span class="hljs-attr">:placeholder</span>=<span class="hljs-string">"placeholder"</span>
      <span class="hljs-attr">:value</span>=<span class="hljs-string">"modelValue"</span>
      @<span class="hljs-attr">input</span>=<span class="hljs-string">"$emit('update:modelValue', $event.target.value)"</span>
    /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>That <code>label</code> value is one of many props that helps this component achieve <strong>reusability</strong>. Along with the data-bound values of <code>type</code>, <code>value</code>, and <code>placeholder</code> (<code>v-bind</code>, or <code>:</code> for short), these props make our component dynamic because they allows us to redefine those values on every occurrence of the component.</p>
<h3 id="vue-props">Vue props</h3>
<p>In all modern frameworks, <code>props</code> define the content of our components (often using strings, numbers, booleans, or objects as data sources). They can also define the functionality of our components (we can pass functions/handlers too 😎). </p>
<p>On first appearance, props will look just like HTML attributes; however, under the hood they're a core piece of what sets a framework apart from plain old HTML, CSS, and JavaScript.</p>
<p>In Vue, component props must be defined in the export object. We can do this by defining them as strings in an array, or as objects with some configuration options.</p>
<pre><code class="lang-jsx"><span class="hljs-comment">// props declared as strings in an array</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'BaseInput'</span>,
  <span class="hljs-attr">props</span>: [<span class="hljs-string">'modelValue'</span>, <span class="hljs-string">'label'</span>, <span class="hljs-string">'placeholder'</span>, <span class="hljs-string">'type'</span>],
}

<span class="hljs-comment">// props declared as objects</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
    <span class="hljs-attr">name</span>: <span class="hljs-string">'BaseInput'</span>,
    <span class="hljs-attr">props</span>: {
        <span class="hljs-attr">modelValue</span>: {
            <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
            <span class="hljs-attr">default</span>: <span class="hljs-string">''</span>,
        },
        <span class="hljs-attr">placeholder</span>: {
            <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
            <span class="hljs-attr">default</span>: <span class="hljs-string">'Placeholder text...'</span>,
        },
        <span class="hljs-attr">type</span>: {
            <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
            <span class="hljs-attr">default</span>: <span class="hljs-string">'text'</span>,
        },
    },
}
</code></pre>
<p>I always prefer the way of objects because I like setting defaults. Plus, I feel secure having some restraints around type setting. It's no TypeScript, but it's better than nothing!</p>
<h3 id="whats-dollaremit">What's <code>$emit</code>?</h3>
<p>Above, you might've noticed this weird looking line on <code>BaseInput.vue</code>:</p>
<pre><code class="lang-jsx">@input=<span class="hljs-string">"$emit('update:modelValue', $event.target.value)"</span>
</code></pre>
<p>This is a custom event handler, just like the <code>@click</code> event we used in our navigation toggle in my last post in this series. </p>
<p>The <code>$emit</code> function inside of that handler allows us to send events <em>up</em> the component tree, and it takes two arguments:</p>
<ol>
<li>The listener that gets defined in the parent component, <code>'update:modelValue'</code>,  </li>
<li>The value we're sending up, which in our case is located on the <code>$event</code> object (our input value).</li>
</ol>
<p>Later when we get to the <strong>v-model</strong> directive, you'll see where the emit value is going.</p>
<p>Now that we've told our component to be on the lookout for these props, and we're <em>emitting</em> the input value back up to the parent, we can finally use the component in <code>Login.vue</code> and begin passing props in.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Login.vue --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter email..."</span>
        <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span>
        <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span>
       /&gt;</span>
       <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
         <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter password..."</span>
         <span class="hljs-attr">label</span>=<span class="hljs-string">"Password"</span>
         <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span>
         <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
       /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Login'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">password</span>: <span class="hljs-string">''</span>,
    }
  },
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Let's break down each of the props:</p>
<ul>
<li><code>placeholder</code> - Because we used data-binding on the HTML placeholder attribute in <code>BaseInput.vue</code>, any string value we pass in here as a prop will fill the input's placeholder value.</li>
<li><code>label</code> - Similar to <code>placeholder</code>, except that it doesn't end up an HTML attribute; it gets rendered to the user as an input label in the component's template.</li>
<li><code>type</code> - We set a default value of "text" in <code>BaseInput.vue</code>, so this value is technically not needed if we want to use a standard text input. However, passing in strings like "password" or "number" allow us to easily change the type of input we'd like to render.</li>
<li><code>v-model</code> - Alright, this deserves its own section...</li>
</ul>
<h3 id="v-model">V-model</h3>
<p>Vue's <strong>v-model</strong> directive applies two-way data binding on user inputs (text, select, checkbox, etc.). It's  <a target="_blank" href="https://vuejs.org/v2/guide/forms.html">described in the Vue docs</a>  as "syntax sugar" (yum 🍫), and is actually shorthand for something that we could just write ourselves.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span> /&gt;</span>

<span class="hljs-comment">&lt;!-- is the same as... --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span>
  <span class="hljs-attr">:modelValue</span>=<span class="hljs-string">"email"</span>
  @<span class="hljs-attr">update:modelValue</span>=<span class="hljs-string">"email = $event"</span>
/&gt;</span>
</code></pre>
<p>Earlier in <code>BaseInput.vue</code> ,we used <code>$emit</code> to send the input value up to the parent component. Now we are:</p>
<ol>
<li>Passing the <code>modelValue</code> prop <em>down</em> to our input component, setting it equal to <code>email</code></li>
<li>Applying the <code>@update</code> listener so that every time the input changes (and <em>emits</em>), we're setting the input value equal to the <code>modelValue</code> we've provided to the input (email or password, in our case).</li>
</ol>
<p><strong>Note</strong>: Writing the <code>v-model</code> longhand syntax on inputs in Vue 3 is a bit different than Vue 2, and it took me a while to figure that out. If you're still on v2, go <a target="_blank" href="https://v3.vuejs.org/guide/migration/v-model.html#overview">check out the docs</a> that lay these changes out pretty clearly.</p>
<p><strong>And with that, our text input component is finished!</strong> But unfortunately our layout's spacing is not in the best shape. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627470549891/DmTFisWpg.jpeg" alt="vue_4_img1_login_no_layout@2x.jpg" /></p>
<p>It's only CSS that we need, but let's explore how to create a styled layout using <strong>slots</strong>.</p>
<h2 id="introducing-slots-in-vue">Introducing slots in Vue</h2>
<p><strong>Slots</strong> allow us to create parent components that take dynamic children. In addition to props, they're a great way to improve a component's reusability, so now we're going to use them to tighten up our mobile layout.</p>
<p>If you're familiar with React, you might know the <code>children</code> prop and how it allows you to compose components meant to contain dynamic child elements. This is basically the equivalent in Vue:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span> <span class="hljs-comment">&lt;!-- or &lt;slot&gt;&lt;/slot&gt; --&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The above is an important use case for when we're making button or card components with a single location to place children. But slots come with some deeper functionality.</p>
<h3 id="named-slots">Named slots</h3>
<p>Named slots allow us to use <em>multiple</em> slots, and it's really as simple as it sounds. By using the <code>name</code> attribute, we can be more targeted in our template markup. This is what we're going to use in our new <code>Layout.vue</code> component, which lives in <code>components/layout/</code>.</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Layout.vue --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"layout"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"header-wrapper"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"header"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"content"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The styling to this layout is minimal and is mostly applied for spacing.</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">div</span><span class="hljs-selector-class">.layout</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">80%</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-selector-class">.header-wrapper</span> {
    <span class="hljs-attribute">margin-top</span>: <span class="hljs-variable">$space8</span>;
    <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-variable">$space7</span>;
  }
}
</code></pre>
<p>When we want to use this component on the Login page, we'll just need to <em>slot</em> in our content with template tags and the <code>v-slot</code> directive, following by a colon and the named slot we're targeting:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Login.vue --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:content</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Form elements go here --&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
</code></pre>
<p>Then we can do the exact same thing in our Register view as well:</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- Register.vue --&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:content</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">form</span>&gt;</span>
            <span class="hljs-comment">&lt;!-- Multi-step form elements go here --&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
</code></pre>
<p><strong>Beautiful!</strong> It's amazing what a little bit of spacing can do. 🌺</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627470832175/Z2bZXfmv8.jpeg" alt="vue_4_img2_login_layout@2x.jpg" /></p>
<h2 id="building-a-button-component">Building a button component</h2>
<p>Finally, we're going to build out a button that we can use on both <code>Login.vue</code> and <code>Register.vue</code>. We'll call it <code>BaseButton.vue</code> and it's going to receive two props that'll handle functionality and appearance.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> 
    <span class="hljs-attr">:variant</span>=<span class="hljs-string">"variant"</span> 
    @<span class="hljs-attr">click</span>=<span class="hljs-string">"onClick"</span>
    <span class="hljs-attr">:class</span>=<span class="hljs-string">"handledVariant"</span>
  &gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">slot</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<ol>
<li><code>variant</code> - The variant will determine the button's appearance. In this case, we just need to handle two values passed in: "primary" and "secondary."</li>
<li><code>@click</code> - The click handler will handle the button's functionality. Providing this as a prop allows us to pass in any function we want from the parent.</li>
</ol>
<p>The class-binding with value, <code>handledVariant</code>, is doing something a bit different...</p>
<h3 id="computed-properties">Computed properties</h3>
<p>Instead of referencing a prop, <code>handledVariant</code> is actually a computed property that we've defined on the component instance. Just like <code>methods</code> and <code>data()</code>, <code>computed</code> is an option that we can include in our export. Its purpose is to help us keep programming logic out of our HTML templates.</p>
<pre><code class="lang-jsx">&lt;script&gt;
    data() {
        <span class="hljs-keyword">return</span> {}
    },
    <span class="hljs-attr">props</span>: {...},
    <span class="hljs-attr">methods</span>: {...},
    <span class="hljs-attr">computed</span>: {
      handledVariant() {
        <span class="hljs-keyword">switch</span> (<span class="hljs-built_in">this</span>.variant) {
          <span class="hljs-keyword">case</span> <span class="hljs-string">"primary"</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"primary"</span>;
          <span class="hljs-keyword">case</span> <span class="hljs-string">"secondary"</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"secondary"</span>;
          <span class="hljs-keyword">default</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"primary"</span>;
      }
    },
  },
&lt;/script&gt;
</code></pre>
<p>Above, we're using a switch statement that returns a string depending on the <code>variant</code> value passed in. That string gets applied as the component's class because we've applied class binding (<code>:class</code>) on the component.</p>
<p>I've also set up a small animation when the component first renders using <code>transform: skew()</code> and <code>box-shadow</code>:</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">button</span> {
  <span class="hljs-attribute">font-family</span>: inherit;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.8rem</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">900</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span> <span class="hljs-number">18px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">margin</span>: <span class="hljs-variable">$space4</span> <span class="hljs-number">0</span>;
  <span class="hljs-attribute">transform</span>: skew(-<span class="hljs-number">3deg</span>, -<span class="hljs-number">3deg</span>);
  &amp;<span class="hljs-selector-class">.primary</span> {
    <span class="hljs-attribute">background</span>: <span class="hljs-variable">$blue500</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$white</span>;
    <span class="hljs-attribute">padding</span>: <span class="hljs-number">8px</span> <span class="hljs-number">24px</span>;
    <span class="hljs-attribute">transform</span>: skew(<span class="hljs-number">0deg</span>, <span class="hljs-number">0deg</span>) translateX(<span class="hljs-number">0px</span>);
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span> <span class="hljs-number">1px</span> <span class="hljs-number">0px</span> rgba(<span class="hljs-variable">$blue500</span>, <span class="hljs-number">0</span>);
    <span class="hljs-attribute">animation</span>: primary <span class="hljs-number">500ms</span> ease <span class="hljs-number">0.5s</span> <span class="hljs-number">1</span> forwards;
  }
  &amp;<span class="hljs-selector-class">.secondary</span> {
    <span class="hljs-attribute">background</span>: rgba(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.8</span>);
    <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-variable">$blue200</span>;
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$blue500</span>;
    <span class="hljs-attribute">transform</span>: skew(<span class="hljs-number">0deg</span>, <span class="hljs-number">0deg</span>) translateX(<span class="hljs-number">0px</span>);
    <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span> <span class="hljs-number">1px</span> <span class="hljs-number">0px</span> rgba(<span class="hljs-variable">$black</span>, <span class="hljs-number">0</span>);
    <span class="hljs-attribute">animation</span>: secondary <span class="hljs-number">500ms</span> ease <span class="hljs-number">0.5s</span> <span class="hljs-number">1</span> forwards;
  }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627471512688/a-anPLe3W.gif" alt="buttons5.gif" /></p>
<p>Now when we want to render <code>BaseButton.vue</code>, it's this simple. 👇</p>
<pre><code class="lang-html"><span class="hljs-comment">&lt;!-- primary style --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span>
  <span class="hljs-attr">:variant</span>=<span class="hljs-string">"primary"</span>
  <span class="hljs-attr">:onClick</span>=<span class="hljs-string">"() =&gt; console.log('do something!')"</span>
&gt;</span>
  Continue
<span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span>&gt;</span>

<span class="hljs-comment">&lt;!-- secondary style --&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span> 
  <span class="hljs-attr">:variant</span>=<span class="hljs-string">"secondary"</span>
  <span class="hljs-attr">:onClick</span>=<span class="hljs-string">"() =&gt; console.log('do something else!')"</span>
&gt;</span>
  Go back
<span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span>&gt;</span>
</code></pre>
<h3 id="event-modifiers">Event modifiers</h3>
<p>Let's finish out this <code>Login.vue</code> component by running a <code>console.log</code> when the form is submitted.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:content</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"handleSubmit"</span>&gt;</span>
        <span class="hljs-comment">&lt;!-- form inputs and buttons go here --&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>We've attached the <code>@submit</code> handler to our form, which gets triggered every time the user clicks the first button (which defaults to type "submit" when placed inside of a form).</p>
<p>Now we just need to make sure our state values, <code>email</code> and <code>password</code>, are getting captured. We can do that by writing a <code>handleSubmit</code> function and using <code>console.log</code> to see the results. </p>
<pre><code class="lang-jsx">&lt;script&gt;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">methods</span>: {
    handleSubmit(e) {
      e.preventDefault();
      <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.email, <span class="hljs-built_in">this</span>.password);
      <span class="hljs-comment">// view the entire data() object with `this.$data`</span>
    }
  }
}
&lt;/script&gt;
</code></pre>
<p>But we actually don't need the <code>e.preventDefault()</code> part. Vue handles preventing the page reload for us when we include the <code>.prevent</code> modifier on the submit handler. It also provides <a target="_blank" href="https://vuejs.org/v2/guide/events.html#Event-Modifiers">a lot of other</a> event modifiers that are all similarly helpful. </p>
<p><strong>And we're done.</strong> You can scroll down to view our finalized <code>Login.vue</code> component. 🙌 </p>
<p>In the next installment, we'll be focused on building a <strong>multi-step form</strong> for user registration. See you then!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1627471810579/sWxb_4Zpm.jpeg" alt="vue_4_img3_login@2x.jpg" /></p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:header</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">template</span> <span class="hljs-attr">v-slot:content</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> @<span class="hljs-attr">submit.prevent</span>=<span class="hljs-string">"handleSubmit"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter email..."</span>
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Email"</span>
          <span class="hljs-attr">v-model</span>=<span class="hljs-string">"email"</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">BaseInput</span> 
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter password..."</span>
          <span class="hljs-attr">label</span>=<span class="hljs-string">"Password"</span>
          <span class="hljs-attr">v-model</span>=<span class="hljs-string">"password"</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
        /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"right-alignment"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">BaseButton</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">BaseButton</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">aside</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"right-alignment"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Need to sign up?<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/register"</span>&gt;</span>Register here.<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">aside</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Login'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">email</span>: <span class="hljs-string">''</span>,
      <span class="hljs-attr">password</span>: <span class="hljs-string">''</span>,
    }
  },
  <span class="hljs-attr">methods</span>: {
    handleSubmit() {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.$data);
    }
  }
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"scss"</span> <span class="hljs-attr">scoped</span>&gt;</span>
aside.right-alignment {
  text-align: right;
  &amp; &gt; div {
    margin-top: $space6;
  }
}
<span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<hr />
<h3 id="you-can-find-me-on-twitterhttpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on <a target="_blank" href="https://twitter.com/matt_pigs">Twitter</a> and <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[Developing a UI with Vue, Part 3: Mobile Navigation]]></title><description><![CDATA[Making mobile navigation has always been fun for me, no matter how many times I do it. I don't know why, but something about designing a menu button and toggling the drawer navigation on and off screen has never stopped feeling like ✨ magic ✨ to me.
...]]></description><link>https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-3-mobile-navigation</link><guid isPermaLink="true">https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-3-mobile-navigation</guid><category><![CDATA[Vue.js]]></category><category><![CDATA[UI]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[scss]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Thu, 15 Jul 2021 16:44:43 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1626438555254/JHX97l_bIA.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Making mobile navigation has always been fun for me, no matter how many times I do it. I don't know why, but something about designing a menu button and toggling the drawer navigation on and off screen has never stopped feeling like ✨ <strong>magic</strong> ✨ to me.</p>
<p>We left off laying the groundwork for our app in <a target="_blank" href="https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-2-getting-grounded-with-appvue">my last post</a>, so let's begin by digging into the mobile <code>Navigation.vue</code> component.</p>
<h2 id="adding-the-burger">Adding the burger 🍔</h2>
<p>When visiting most websites you'll often see menus, especially on mobile, become hidden behind a toggle button that looks like three lines stacked on top of one another. Semantically, these three lines represent a list of items like you'd see in any navigation menu. And as you might guess, they sorta look like a cheeseburger. <strong>See how similar they are?</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626258492923/A3yD_66zy.png" alt="ilovue – 2@2x.png" /></p>
<p>We'll start by marking up the burger in HTML, where we'll be using a two-line approach for simplicity:</p>
<pre><code>  <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"burger"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Toggle Menu"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre><p>And then we'll do the following: </p>
<ul>
<li>Place the burger markup right inside of our <code>&lt;nav&gt;</code> element. </li>
<li>Wrap our <code>&lt;ul&gt;</code> in a container element. We'll use this to manipulate its style and behavior later on.</li>
<li>Add an <code>&lt;img&gt;</code> element as the first child of our nav. We'll link the logo as our <code>src</code> attribute.</li>
</ul>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Main Navigation"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"../../assets/logo.png"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"82"</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"list-container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/register"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"burger"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Mobile Navigation Toggle Button"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre><p>Once we have the burger in our template, we can style it:</p>
<ul>
<li>Begin by removing default button styles and setting the button dimensions.</li>
<li>Define the base styles for each line in our burger (we have two of them). They're each going to be the full width of the button, but only a couple of pixels thick, separated by a margin above and below.</li>
<li>The <code>&amp;.open</code> syntax is a SCSS selector that styles the element when it has <em>both</em> classes. So when the <code>burger</code> class element also contains the <code>open</code> class, the lines are going to reposition themselves to form an ❌. </li>
</ul>
<pre><code class="lang-scss"><span class="hljs-selector-tag">button</span><span class="hljs-selector-class">.burger</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">background</span>: none;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">38px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
  <span class="hljs-attribute">z-index</span>: <span class="hljs-number">100</span>;
  <span class="hljs-selector-class">.line</span> {
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">2px</span>;
    <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">20px</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-variable">$gray200</span>;
    <span class="hljs-attribute">margin</span>: <span class="hljs-number">12px</span> <span class="hljs-number">0</span>;
    <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.4s</span> ease-out;
  }
  &amp;<span class="hljs-selector-class">.open</span> {
    <span class="hljs-selector-class">.line-1</span> {
      <span class="hljs-attribute">transform</span>: translateY(<span class="hljs-number">7px</span>) rotate(<span class="hljs-number">45deg</span>);
    }
    <span class="hljs-selector-class">.line-2</span> {
      <span class="hljs-attribute">transform</span>: translateY(-<span class="hljs-number">7px</span>) rotate(-<span class="hljs-number">45deg</span>);
    }
  }
}
</code></pre>
<p><strong>Tip</strong>: I perfected the transform positioning by placing a border on the button and making edits in Chrome's Dev Tools. It's much easier to do there than to work directly in the code editor.</p>
<p>Once we're finished, we'll end up with something like this 👇</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626259993610/JyelUnQ7B.png" alt="vue_3_img_1_structured mock-up@2x.png" /></p>
<h2 id="styling-the-navigation-menu">Styling the navigation menu 🗺️</h2>
<p>In order to move on, we'll want to begin styling that <code>.list-container</code> class we wrapped around our <code>&lt;ul&gt;</code> element:</p>
<pre><code class="lang-scss">  <span class="hljs-selector-class">.list-container</span> {
    <span class="hljs-attribute">display</span>: none;
    <span class="hljs-attribute">opacity</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
    <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
    <span class="hljs-attribute">position</span>: absolute;
    <span class="hljs-attribute">left</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
    <span class="hljs-attribute">background</span>: <span class="hljs-variable">$blue100</span>;
    <span class="hljs-attribute">z-index</span>: <span class="hljs-number">99</span>;
    <span class="hljs-attribute">transition</span>: all <span class="hljs-number">0.4s</span> ease-out;
    &amp;<span class="hljs-selector-class">.open</span> {
      <span class="hljs-keyword">@include</span> flex(row, center, center);
      <span class="hljs-attribute">opacity</span>: <span class="hljs-number">1</span>;
    }
  }
</code></pre>
<p>Let's break down what this is doing exactly:</p>
<ul>
<li><code>display</code> and <code>opacity</code> set a hidden default state</li>
<li><code>height</code> and <code>width</code> fill the user's viewport</li>
<li><code>position: absolute</code> is removing the element from the flow of the page</li>
<li><code>left</code> and <code>top</code> do something different depending on the <code>position</code> property. In our case, these properties set the distance between the element and the closest parent with <code>relative</code> positioning. <strong>Ours is the root <code>#app</code> element.</strong> Along with <code>height</code> and <code>width</code>, these properties are what make our mobile navigation fill the user's screen.</li>
<li>Finally, <code>z-index</code> brings the element to the forefront of our user's experience. The <code>transition</code> property is for later when we're toggling between classes and we want a smooth visual effect.</li>
</ul>
<p>I also want to point out that <strong>Vue Router</strong> gives us an "active" class, which we'll use to style the currently selected route:</p>
<pre><code><span class="hljs-selector-tag">a</span> {
  <span class="hljs-attribute">text-decoration</span>: none;
  <span class="hljs-attribute">color</span>: inherit;
  &amp;<span class="hljs-selector-class">.router-link-exact-active</span> {
    <span class="hljs-attribute">color</span>: <span class="hljs-variable">$blue500</span>;
  }
}
</code></pre><p>With the addition of some base styles applied to our menu items (I used flexbox to center the div, everybody), our <code>.list-container</code> menu will appear to the user as you see below; the parent <code>&lt;nav&gt;</code> element stays in the forefront using <code>z-index</code> so that we can still access the toggle.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626261763538/kMfHyTFKs.png" alt="vue_3_img_2_navigation@2x.png" /></p>
<h2 id="where-vue-comes-in">Where Vue comes in 💚</h2>
<p>In <strong>React</strong> we have state: a set of properties which control a component's behavior. State is typically stored and updated with the <code>useState</code> hook, or alternatively, within <code>this.state</code> in class-based components. </p>
<p>In <strong>Vue</strong>, state is represented with the <code>data</code> object (it's really a function that returns an object, but you get it). Once we begin adding properties, Vue "pulls [this object] into its reactivity system and stores it on the component instance as <code>$data</code>." That's straight from the docs.</p>
<p>If you're confused like I was about the <code>$</code> prefixes in the Vue ecosystem, this excerpt from the Vue documentation might help:</p>
<blockquote>
<p>Vue uses a $ prefix when exposing its own built-in APIs via the component instance. It also reserves the prefix _ for internal properties.</p>
</blockquote>
<p>This is why you might see similar references like <code>$store</code> or <code>$route</code> in Vue components. They're just special Vue APIs that help you do stuff 😇</p>
<h3 id="vues-data-and-method-options">Vue's data and method options</h3>
<p>The only property our Navigation component needs is one that tracks the menu's open status. We'll initialize this property with a boolean value of <code>false</code> and call it <code>menuOpen</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Navigation'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">menuOpen</span>: <span class="hljs-literal">false</span>,
    }
  },
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Now, we need to handle the toggling of this value. We'll do this by adding a <code>methods</code> options to our component instance and create our first Vue method! </p>
<p>Vue makes it easier for us to reference <code>data</code> values by binding <code>this</code> for methods, so and we need to do is define <code>handleToggleMenu()</code> and use the logical NOT operator to flip the boolean value of <code>menuOpen</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Navigation'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">menuOpen</span>: <span class="hljs-literal">false</span>,
  },
  <span class="hljs-attr">methods</span>: {
    handleToggleMenu() {
      <span class="hljs-built_in">this</span>.menuOpen = !<span class="hljs-built_in">this</span>.menuOpen
    },
  },
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h3 id="using-v-directives-in-the-template">Using v-directives in the template</h3>
<p>We're going to bring this component full circle by helping our state and methods <strong>define component style and behavior</strong>, thus driving the user experience. We can do that by turning to v-directives, in particular <code>v-bind</code>.</p>
<p>Let's focus first on the <code>.list-container</code> element. In addition to the normal class attribute we'd see in typical HTML, we can bind class values with the syntax below.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"navigation-wrapper"</span> <span class="hljs-attr">v-bind:class</span>=<span class="hljs-string">"{ open: menuOpen }"</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/register"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>First, as someone who came to Vue from React, I know the string syntax can be a little disorienting at first. I'm used to using brackets in JSX (i.e. <code>className={}</code>) and inserting some conditional logic. However the class binding acts in a very interesting way.</p>
<p>With <code>v-bind:class</code> (or <code>:class</code> for short), what Vue does is measure the truthiness of a property's value inside of the object. So in this example:</p>
<ol>
<li>We provide an object - <code>{ open: menuOpen }</code>.</li>
<li>Vue sees the <code>open</code> property and its corresponding value <code>menuOpen</code></li>
<li>If <code>menuOpen</code> is truthy, <code>open</code> is applied as a class, otherwise nothing happens.</li>
</ol>
<p><strong>Note</strong>: It's also important to point out that Vue makes it so that all properties are made available from within a component's template, so we're able to reference <code>menuOpen</code> and <code>handleToggleMenu</code> with ease.</p>
<p>We can go ahead and replicate this functionality on the burger button element with the addition of the <code>@click</code> property.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
  <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Mobile Navigation Button"</span>
  <span class="hljs-attr">class</span>=<span class="hljs-string">"burger"</span>
  <span class="hljs-attr">v-bind:class</span>=<span class="hljs-string">"{ open: menuOpen }"</span>
  @<span class="hljs-attr">click</span>=<span class="hljs-string">"handleToggleMenu"</span>
&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"line line-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Similar to React's <code>onClick</code> event handler, we're able to pass a function to the <code>@click</code> handler in order to handle the user's action. This is just one of Vue's many event handlers, including <code>@scroll</code>, <code>@keyup</code>, <code>@keydown</code>, and even <code>@submit</code>. Vue also has  <a target="_blank" href="https://v3.vuejs.org/guide/events.html#event-modifiers">event modifiers</a>  which are <em>very</em> cool and worth checking out.</p>
<p>When we click the burger button now, our menu toggles on and off screen like it should!</p>
<h2 id="cascading-animation-with-scss">Cascading animation with SCSS 💅</h2>
<p>There's one last thing I'd like to do, and that's apply a cascading effect to each individual list item using a <code>@keyframes</code> animation. We could accomplish this by hard coding some classes in CSS, but SCSS gives us a much better way.</p>
<h3 id="looping-with-scss">Looping with SCSS</h3>
<p>SCSS gives us a very easy way to generate as many classes as we'd like with just a few lines of code. We can do this by defining a <code>for</code> loop inside of our <code>&lt;li&gt;</code> selector.</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">li</span> {
  <span class="hljs-keyword">@for</span> <span class="hljs-variable">$i</span> from <span class="hljs-number">1</span> through <span class="hljs-number">8</span> {
    &amp;<span class="hljs-selector-class">.slide-in-</span>#{<span class="hljs-variable">$i</span>} {
      <span class="hljs-attribute">animation</span>: nav-items-enter <span class="hljs-number">500ms</span> ease #{<span class="hljs-variable">$i</span> / <span class="hljs-number">12</span>}s <span class="hljs-number">1</span> forwards;
    }
  }
}
</code></pre>
<p>Let's break this down...</p>
<ol>
<li>We set a variable to loop over and use the <code>&amp;.[class-name]</code> syntax in order to create classes for <code>&lt;li&gt;</code> tags.</li>
<li>We use the variable we're looping with directly within the class names. This way, we can differentiate between each class. This loop is going to generate <code>.slide-in-1</code>, <code>.slide-in-2</code>, <code>.slide-in-3</code>, and so on.</li>
<li>The cascade comes in when setting our element's delay, so that each consecutive class is scheduled to begin animating at slightly later times than the last. The first element comes in at <code>1/12s</code>, then next at <code>2/12s</code>, then <code>3/12s</code>. You get it 😉</li>
</ol>
<p><strong>But wait</strong>. How do we apply these classes? We could hard code them into our HTML template. But this is the perfect time to introduce lifecycle hooks in Vue.</p>
<h2 id="vues-lifecycle-hooks">Vue's lifecycle hooks ⚓</h2>
<p>The lifecycle of a component is an important thing to consider when developing with Vue. <strong>Lifecycle hooks</strong> give us windows into moments of time for a component; from when it's created and mounted to the DOM, to when it's updated or removed from view completely.</p>
<p>The below diagram from the official Vue.js documentation may make the component lifecycle more clear:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1626365696812/73Nx-F0UE.png" alt="XLvznbh.png" /></p>
<p>So just like we have for <code>data()</code> and <code>methods()</code>, we can add the <code>mounted()</code> hook to our component instance and write some good old JavaScript to loop over each of our nav items.</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'Navigation'</span>,
  data() {
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">menuOpen</span>: <span class="hljs-literal">false</span>,
    }
  },
  mounted() {
    <span class="hljs-keyword">const</span> navItems = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'.list-container li'</span>)
    navItems.forEach(<span class="hljs-function">(<span class="hljs-params">item, index</span>) =&gt;</span> {
      item.classList.add(<span class="hljs-string">`slide-in-<span class="hljs-subst">${index + <span class="hljs-number">1</span>}</span>`</span>)
    })
  },
  <span class="hljs-attr">methods</span>: {
    handleToggleMenu() {
      <span class="hljs-built_in">this</span>.menuOpen = !<span class="hljs-built_in">this</span>.menuOpen
    },
  },
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><p><strong>Tip</strong>: We don't even really need the <code>mounted()</code> method to apply those classes. I just wanted to use a lifecycle hook! Alternatively, we can just use the CSS <code>:nth-child()</code> selector and loop through that, like this 👇</p>
<pre><code><span class="hljs-selector-tag">li</span> {
  <span class="hljs-keyword">@for</span> <span class="hljs-variable">$i</span> from <span class="hljs-number">1</span> through <span class="hljs-number">8</span> {
    &amp;<span class="hljs-selector-pseudo">:nth-child</span>(#{<span class="hljs-variable">$i</span>}) {
      <span class="hljs-attribute">animation</span>: nav-items-enter <span class="hljs-number">500ms</span> ease #{<span class="hljs-variable">$i</span> / <span class="hljs-number">12</span>}s <span class="hljs-number">1</span> forwards;
    }
  }
}
</code></pre><p>And <em>viola</em>! We have a mobile menu that toggles, and a nice little cascading animation effect for the menu items as well 🥳</p>
<p><strong>Next up, I'm gonna be building custom input and button components. Hope to see you there 👋</strong></p>
<hr />
<h3 id="you-can-find-me-on-twitterhttpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on <a target="_blank" href="https://twitter.com/matt_pigs">Twitter</a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[What the Heck is Inversion of Control?]]></title><description><![CDATA[Photo by  Noah Buscher on Unsplash 
Recently, I was listening in on a Twitter Space while building a React component. This egghead.io space was focused on learning and teaching React, and some very experienced folks like Kent C. Dodds were there to s...]]></description><link>https://blog.mattpignatore.dev/what-the-heck-is-inversion-of-control</link><guid isPermaLink="true">https://blog.mattpignatore.dev/what-the-heck-is-inversion-of-control</guid><category><![CDATA[React]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[react router]]></category><category><![CDATA[ReactHooks]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Fri, 09 Jul 2021 18:39:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1625855845536/QzGmSkyXV.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Photo by  <a target="_blank" href="https://unsplash.com/@noahbuscher">Noah Buscher on Unsplash</a> </p>
<p>Recently, I was listening in on a Twitter Space while building a React component. This egghead.io space was focused on learning and teaching React, and some very experienced folks like <a target="_blank" href="https://kentcdodds.com/">Kent C. Dodds</a> were there to spread their wealth of knowledge about the framework.</p>
<p>At one point Kent mentioned <strong>inversion of control</strong>, something I had never heard of. I barely even made out what he was saying, so I began Googling what I thought it sounded like. Luckily I was close enough, and <a target="_blank" href="https://cdn.hashnode.com/res/hashnode/image/upload/v1625827260678/3jhAey-ld.png">came across his post</a> that covers the concept in detail.  </p>
<p>If I understand it correctly (and if I don't,  <a target="_blank" href="https://www.twitter.com/matt_pigs">please tell me</a>  😭), inversion of control is a programming concept that allows us to <strong>abstract</strong> away logic from a component so that it fits more use cases, "inverting control" of that logic. </p>
<p>Writing reusable components is a valuable skill in React, and I happened to be running into a problem in that moment that inversion of control might solve.</p>
<h2 id="the-reusable-table-component">The reusable table component ♻️</h2>
<p>The component I was building happened to be pretty complex, and I wanted to make it fit for multiple modules in the project I was working on. The component makes use of a table package called <strong>React-Table</strong>, which is a hooks-based table library that comes with all the functionality you'd want in a table and <strong>no actual UI elements</strong>.</p>
<p><strong>Sidenote</strong>: This was Very Exciting for me because sometimes I <em>loathe</em> pulling pre-defined styles into a project only to re-style them to fit the UI.</p>
<p>My problem arose while trying to wrap some React Router anchor <code>&lt;Link&gt;</code> tags around <code>&lt;td&gt;</code> elements:</p>
<pre><code class="lang-javascript">&lt;td {...cell.getCellProps()}&gt;
  {
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{</span>`/<span class="hljs-attr">employees</span>/<span class="hljs-attr">edit</span>/${<span class="hljs-attr">row.values.id</span>}`}&gt;</span>
      // render the cell contents
      cell.render('Cell')
    <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span></span>
  }
&lt;/td&gt;
</code></pre>
<p><strong>React Router</strong> provides us a great way to <a target="_blank" href="https://reactrouter.com/web/example/url-params">handle the dynamic route</a>, but how do we make the link itself dynamic? We need to be able to swap out the entire string if we want to reuse this table. We also don't know for sure that the interpolated JS is always going to be <code>rows.value.id</code>, so that needs to be dynamic too!</p>
<h2 id="lets-try-inverting-control">Let's try "inverting control" 🕹️</h2>
<p>As I read in the aforementioned write-up by Kent C. Dodds, we can invert control by passing a function down as a prop from the parent component. Note that this isn't a one-to-one comparison of the example he used in the article, but I believe the concept is useful in my case as well.</p>
<p>The function prop allows us to pass the row data into logic that's going to be defined in the parent.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> ReusableTable = <span class="hljs-function">(<span class="hljs-params">{ linkFn }</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="hljs-comment">// the beginning of the table...</span>
    {rows.map(<span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> (
      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">td</span> {<span class="hljs-attr">...cell.getCellProps</span>()}&gt;</span>
        {
          <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">{linkFn(row)}</span>&gt;</span>
            // render the cell contents
            cell.render('Cell')
          <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        }
      <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span></span>
    ))}
    <span class="hljs-comment">// the rest of the table...</span>
  )
}
</code></pre>
<p>Now in the parent, an anonymous function is passed to our ReusableTable as <code>linkFn</code>, just like we'd see for an onClick handler, for example. The function will take one argument, which is going to be the row we're passing in. All we'll have it do is return the string interpolation after gaining access to the row data within the table.</p>
<pre><code class="lang-javascript">
&lt;ReusableTable linkFn={<span class="hljs-function"><span class="hljs-params">passedRow</span> =&gt;</span> (
  <span class="hljs-string">`/employees/edit/<span class="hljs-subst">${passedRow.values.id}</span>`</span>
)} /&gt;
</code></pre>
<p>Now when we render this table in other modules, we'll be able to provide whatever path necessary with whatever interpolated JS we need:</p>
<pre><code><span class="hljs-comment">// employees module</span>
&lt;ReusableTable linkFn={<span class="hljs-function"><span class="hljs-params">passedRow</span> =&gt;</span> (
  <span class="hljs-string">`/employees/edit/<span class="hljs-subst">${passedRow.values.id}</span>`</span>
)} /&gt;

<span class="hljs-comment">// projects module</span>
&lt;ReusableTable linkFn={<span class="hljs-function"><span class="hljs-params">passedRow</span> =&gt;</span> (
  <span class="hljs-string">`/projects/<span class="hljs-subst">${passedRow.values.project_id}</span>`</span>
)} /&gt;

<span class="hljs-comment">// customers module</span>
&lt;ReusableTable linkFn={<span class="hljs-function"><span class="hljs-params">passedRow</span> =&gt;</span> (
  <span class="hljs-string">`/customers/edit/<span class="hljs-subst">${passedRow.values.customer_id}</span>`</span>
)} /&gt;
</code></pre><p>I've literally just scratched the surface of this concept, and am excited to become more familiar with it especially in practice. The above is an extremely simple example, sure, but I'm convinced the concept is going to be appearing again and again as I dig deeper into this project and into React.</p>
<hr />
<h3 id="you-can-find-me-on-twitter-httpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on  <a target="_blank" href="https://twitter.com/matt_pigs">Twitter </a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[Developing a UI with Vue, Part 2: Project Setup and Design System]]></title><description><![CDATA[In my last post, I  introduced the UI I'm going to be building for my first full-on side project with Vue. We did some basic setup of the project using the Vue CLI, but now we're going to get grounded with a design system for Medo, our movie watch li...]]></description><link>https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-2-project-setup-and-design-system</link><guid isPermaLink="true">https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-2-project-setup-and-design-system</guid><category><![CDATA[Vue.js]]></category><category><![CDATA[UI]]></category><category><![CDATA[UI Design]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[javascript framework]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Mon, 05 Jul 2021 21:54:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1626438511956/YpWy6TkxB.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In my last post, I  <a target="_blank" href="https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project">introduced the UI I'm going to be building</a> for my first full-on side project with Vue. We did some basic setup of the project using the Vue CLI, but now we're going to get grounded with a design system for <strong>Medo</strong>, our movie watch list app.</p>
<p>You'll want to be familiar with <strong>HTML</strong>, <strong>SCSS</strong>, <strong>Flexbox</strong>, and the basics of <strong>front-end frameworks</strong> (components, state, props, etc.) in order to follow along. If you're not, head over to <a target="_blank" href="https://www.freecodecamp.org/">freecodecamp.org</a> or something and dive in 🏊 The best way to learn is to build!</p>
<p>But first...</p>
<h2 id="a-basic-design-system">A basic design system 🎨</h2>
<p>Lately I've made it a habit to begin projects by <strong>defining a design system up front</strong>.</p>
<p>The system I use is just a set of variables to be used throughout the application. The purpose it serves is not only for consistency in design, but it also makes our lives much easier: we'll no longer be getting bogged down choosing between <code>10px</code> and <code>12px</code> of padding, or between <code>#this</code> or <code>#that</code> shade of blue.</p>
<p>I came across this tip in a really great e-book called <a target="_blank" href="https://www.refactoringui.com/">Refactoring UI</a>, written by <strong>Adam Wathan</strong> and <strong>Steve Schoger</strong> of Tailwind fame.</p>
<p>They reason that, by limiting our options for spacing, font-size, color, etc. in our design, we're reducing the need to focus on these small decisions in the future.</p>
<blockquote>
<p>A simple approach is to start with a sensible base value, then build a scale using factors and multiples of that value.</p>
<p>16px is a great number to start with because it divides nicely, and also happens to be the default font size in every major web browser.</p>
<p>The values at the small end of the scale should start pretty packed together, and get progressively more spaced apart as you get further up the scale.</p>
</blockquote>
<hr />
<p>You can check out the design system I'm using for <strong>Medo</strong> through the <a target="_blank" href="https://github.com/mpaitgt/medo-app">Github repo</a> I have set up, but it generally looks something like this:</p>
<pre><code class="lang-scss"><span class="hljs-comment">// color</span>
<span class="hljs-variable">$white</span>: <span class="hljs-number">#ffffff</span>;
<span class="hljs-variable">$blue100</span>: <span class="hljs-number">#f0f5ff</span>;
<span class="hljs-variable">$blue200</span>: <span class="hljs-number">#c9ddff</span>;
<span class="hljs-variable">$gray100</span>: <span class="hljs-number">#d6d6d6</span>;
<span class="hljs-variable">$gray200</span>: <span class="hljs-number">#7c89a1</span>;

<span class="hljs-comment">// space </span>
<span class="hljs-variable">$space1</span>: <span class="hljs-number">0.4rem</span>;
<span class="hljs-variable">$space2</span>: <span class="hljs-number">0.8rem</span>;
<span class="hljs-variable">$space3</span>: <span class="hljs-number">1.2rem</span>;
<span class="hljs-variable">$space4</span>: <span class="hljs-number">1.6rem</span>;

<span class="hljs-comment">// text size</span>
<span class="hljs-variable">$text1</span>: <span class="hljs-number">1.2rem</span>;
<span class="hljs-variable">$text2</span>: <span class="hljs-number">1.4rem</span>;
<span class="hljs-variable">$text3</span>: <span class="hljs-number">1.6rem</span>;
<span class="hljs-variable">$text4</span>: <span class="hljs-number">1.8rem</span>;

<span class="hljs-comment">// gradients</span>
<span class="hljs-variable">$bg</span>: linear-gradient(<span class="hljs-number">180deg</span>, white, <span class="hljs-variable">$blue150</span>);
</code></pre>
<p>I also often use mixins, which are just SCSS functions that take optional arguments (CSS properties and values) and spit out chunks of CSS code. I use Flexbox a lot, so this one has been particularly time saving:</p>
<pre><code class="lang-scss"><span class="hljs-keyword">@mixin</span> flex(
  <span class="hljs-variable">$direction</span>: row, 
  <span class="hljs-variable">$justify</span>: flex-start, 
  <span class="hljs-variable">$align</span>: flex-start, 
  <span class="hljs-variable">$grow</span>: <span class="hljs-number">0</span>
) {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: <span class="hljs-variable">$direction</span>;
  <span class="hljs-attribute">justify-content</span>: <span class="hljs-variable">$justify</span>;
  <span class="hljs-attribute">align-items</span>: <span class="hljs-variable">$align</span>;
  <span class="hljs-attribute">flex-grow</span>: <span class="hljs-variable">$grow</span>;
}

<span class="hljs-comment">// example use case</span>
<span class="hljs-selector-class">.some-flex-container</span> {
  <span class="hljs-keyword">@include</span> flex(row, justify-between, center);
}
</code></pre>
<p>Mixins are also great for things like <a target="_blank" href="https://css-tricks.com/approaches-media-queries-sass/">media queries</a> too! </p>
<p>I've been re-using these really simple design system settings in several projects recently and have found them very valuable! Highly recommended.</p>
<hr />
<h2 id="my-first-mobile-first-app">My First Mobile-First App 📱</h2>
<p>Since my work on web apps hasn't required any mobile-friendly designs so far, I haven't developed enough as I'd like for smaller screen sizes. So, I want to take this chance to design this UI <em>exclusively</em> for mobile. Let's make that happen with a little bit of (S)CSS:</p>
<pre><code class="lang-scss"><span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#a7cdff</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.0rem</span>;
  <span class="hljs-attribute">font-family</span>: Lane, Helvetica, Arial, sans-serif;
  -webkit-<span class="hljs-attribute">font</span>-smoothing: antialiased; <span class="hljs-comment">// default preset with Vue CLI </span>
  -moz-osx-<span class="hljs-attribute">font</span>-smoothing: grayscale; <span class="hljs-comment">// default preset with Vue CLI</span>
}
<span class="hljs-selector-id">#app</span> {
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">428px</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-variable">$bg</span>;
  <span class="hljs-attribute">background-attachment</span>: fixed;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span> <span class="hljs-number">10px</span> -<span class="hljs-number">2px</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0.16</span>);
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">overflow</span>: scroll;
}
</code></pre>
<p>We’ll also set up a base template for our app in <code>App.vue</code>, which will house our <code>&lt;nav&gt;</code> element containing <code>&lt;router-link&gt;</code> tags, as well as the <code>&lt;router-view /&gt;</code>.</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"nav"</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Main Navigation"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">router-link</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/register"</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">router-link</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span> /&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>We'll finish the nav off for now by extracting it to its own component called <code>Navigation.vue</code> in <code>src/components/layout</code>.</p>
<p>If the above route tags look unfamiliar to you, they're likely your introduction to <a target="_blank" href="https://router.vuejs.org/">Vue Router</a>. </p>
<h2 id="routing-with-vue">Routing with Vue 🛣️</h2>
<p>Vue Router, like <strong>React Router</strong>, handles all of the users' URL requests for new pages and sub-pages. The <code>&lt;router-view /&gt;</code> you see in the template above is the window into each of our app's different pages/views, and the <code>&lt;router-link&gt;</code> tags are just anchor tags made special. </p>
<p>Rather than making a <strong>server request</strong> when new routes are visited through anchor tags (which is the default behavior of the browser), Vue Router makes the tags behave so that they're just <strong>rendering components</strong> instead; no page loads or server requests required.</p>
<p>In order to define our <strong>Medo</strong> routes, we utilize the <code>index.js</code> file in <code>router/</code> and import our views. <strong>Since we selected to use Vue Router when we used Vue CLI for setup, the router has come already configured.</strong> All we need to do is import our own views, <code>Login.vue</code> and <code>Register.vue</code>, and define them in the routes array:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { createRouter, createWebHistory } <span class="hljs-keyword">from</span> <span class="hljs-string">'vue-router'</span>
<span class="hljs-keyword">import</span> Login <span class="hljs-keyword">from</span> <span class="hljs-string">'../views/Login.vue'</span>
<span class="hljs-keyword">import</span> Register <span class="hljs-keyword">from</span> <span class="hljs-string">'../views/Register.vue'</span>

<span class="hljs-keyword">const</span> routes = [
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/'</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'Login'</span>,
    <span class="hljs-attr">component</span>: Login,
  },
  {
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/register'</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'Register'</span>,
    <span class="hljs-attr">component</span>: Register,
  },
]

<span class="hljs-keyword">const</span> router = createRouter({
  <span class="hljs-attr">history</span>: createWebHistory(process.env.BASE_URL),
  routes,
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> router
</code></pre>
<p><strong>Note</strong>: I'm making the root of our app, <code>/</code>, route directly to the login page. In the future, we may make it so that this a marketing page that advertises features with a sign up CTA.</p>
<h2 id="the-flex-footer">The Flex Footer 🦶</h2>
<p>The final piece I want to cover is the footer, which will be appearing to the user on any public pages the user can view when <em>not</em> logged in. We're going to place that in the <code>App.vue</code> template just below the router, with a couple of changes to our HTML mark-up:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Navigation</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">role</span>=<span class="hljs-string">"navigation"</span> <span class="hljs-attr">aria-label</span>=<span class="hljs-string">"Footer Navigation"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"./assets/logo.png"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Medo logo"</span> <span class="hljs-attr">width</span>=<span class="hljs-string">"76"</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">nav</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>About<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Contact<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"copyright"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span><span class="hljs-symbol">&amp;copy;</span> 2021, Medo<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>We're doing a few things here:</p>
<ol>
<li><strong>Wrapping the entire template in a div with class "page-container".</strong> This allows us to apply Flexbox to the app container, which has a <code>min-height: 100vh</code>.</li>
<li><strong>Wrapping the <code>&lt;router-view&gt;</code> element in a div with class "page".</strong> This class sets <code>flex-grow: 1</code>, pushing the footer to the bottom (as long as Step 1 is applied) no matter what content is placed here.</li>
<li><strong>Adding our footer markup.</strong> This includes an app logo, future navigation items, and a copyright tag.</li>
</ol>
<p>Like we did with Navigation.vue, we'll extract the footer to its own component, so that the <code>App.vue</code> template looks like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page-wrapper"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Navigation</span> /&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"page"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">router-view</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Footer</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>
</code></pre>
<p>The script tag looks like this:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">import</span> Navigation <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/layout/Navigation'</span>
<span class="hljs-keyword">import</span> Footer <span class="hljs-keyword">from</span> <span class="hljs-string">'@/components/layout/Footer'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">'App'</span>,
  <span class="hljs-attr">components</span>: {
    Navigation,
    Footer,
  },
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>And the style tag looks like this:</p>
<pre><code class="lang-scss">&lt;<span class="hljs-selector-tag">style</span> lang="scss"&gt;
* {
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">box-sizing</span>: border-box;
}
<span class="hljs-selector-tag">html</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">62.5%</span>;
}
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">2.0rem</span>;
  <span class="hljs-attribute">font-family</span>: Lane, Helvetica, Arial, sans-serif;
  -webkit-<span class="hljs-attribute">font</span>-smoothing: antialiased;
  -moz-osx-<span class="hljs-attribute">font</span>-smoothing: grayscale;
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#a7cdff</span>;
}
<span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-tag">h4</span>,
<span class="hljs-selector-tag">h5</span>,
<span class="hljs-selector-tag">h6</span> {
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">100</span>;
}
<span class="hljs-selector-tag">p</span> {
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
}
<span class="hljs-selector-tag">ul</span> {
  <span class="hljs-attribute">list-style</span>: none;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
}
<span class="hljs-selector-id">#app</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-variable">$bg</span>;
  <span class="hljs-attribute">background-attachment</span>: fixed;
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">428px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">100vh</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">position</span>: relative;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0px</span> <span class="hljs-number">0px</span> <span class="hljs-number">10px</span> -<span class="hljs-number">2px</span> rgba(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">0.16</span>);
  <span class="hljs-attribute">overflow</span>: scroll;
}
<span class="hljs-comment">/* Hide scrollbarfor Chrome, Safari and Opera */</span>
<span class="hljs-selector-id">#app</span>::-webkit-scrollbar {
  display: none;
}
<span class="hljs-comment">/* Hide scrollbar for IE, Edge and Firefox */</span>
<span class="hljs-selector-id">#app</span> {
  -ms-<span class="hljs-attribute">overflow</span>-<span class="hljs-selector-tag">style</span>: none;  <span class="hljs-comment">/* IE and Edge */</span>
  scrollbar-<span class="hljs-attribute">width</span>: none;  <span class="hljs-comment">/* Firefox */</span>
}
<span class="hljs-selector-class">.page-wrapper</span> {
  <span class="hljs-keyword">@include</span> flex(column);
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
}
<span class="hljs-selector-class">.page</span> {
  <span class="hljs-attribute">flex-grow</span>: <span class="hljs-number">1</span>;
}
&lt;/<span class="hljs-selector-tag">style</span>&gt;
</code></pre>
<p><strong>And we're finished!</strong></p>
<p>In my next post, I'll be focused on developing <strong>mobile navigation</strong> and introducing state into our application for the first time. In the meantime, check out  <a target="_blank" href="https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project">what led me to choose Vue</a>.</p>
<hr />
<h3 id="you-can-find-me-on-twitter-httpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on  <a target="_blank" href="https://twitter.com/matt_pigs">Twitter </a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[Developing a UI with Vue, Part 1: Introducing the Project]]></title><description><![CDATA[React was the first front-end frameworks I got to know. That was a year and a half ago, and I've really enjoyed learning and growing with it. Sure... there are still some things I still don't fully understand, but I'm complicated too! Who am I to jud...]]></description><link>https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project</link><guid isPermaLink="true">https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project</guid><category><![CDATA[Vue.js]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[side project]]></category><category><![CDATA[Firebase]]></category><category><![CDATA[UI]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Tue, 29 Jun 2021 00:22:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1626438469806/_s0OmbcOJI.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>React was the first front-end frameworks I got to know. That was a year and a half ago, and I've really enjoyed learning and growing with it. Sure... there are still some things I still don't fully understand, but I'm complicated too! Who am I to judge? 🤷‍♂️</p>
<p>However, last year I was brought onto a project using <strong>jQuery</strong> where I was writing a lot of CSS, and it had me wondering whether I liked writing CSS in React at all. That's when I began looking into other frameworks, and developed a crush on Vue. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624567167857/3VZ1pVf7x.jpeg" alt="ilovue.jpg" /></p>
<h2 id="why-i-was-crushing-on-vue">Why I was crushing on Vue 💚</h2>
<p>As a UI Developer I write a lot of CSS, and none of React's solutions were appealing to me. I tried them all: CSS-in-JS, modules, styled-components...all of them had things about them that I didn't quite like (<strong>Note</strong>: I've since come around to  <a target="_blank" href="https://styled-components.com/docs/advanced">theming with styled-components</a> , and it's now my preferred method in React 👍).</p>
<p>But there were other reasons too:</p>
<ul>
<li><strong>I felt that I had hit a wall in React.</strong> I began struggling to learn some advanced topics and, frankly, I was trying to move too fast in both the front- and back-end.</li>
<li><strong>Vue wasn't backed by Big Tech.</strong> I don't <em>love</em> the power that Big Tech wields, so I admired the fact that Vue is one of the independent frameworks (I see you too, Svelte).</li>
<li><strong>I was excited to learn a new framework from square one.</strong> The "shiny new object" phenomenon is real! It's easy to get attracted to new tech, and I liked the idea of starting fresh with a new framework.</li>
</ul>
<p>So with all of this bubbling up, I checked out the notorious <a target="_blank" href="https://vuejs.org/v2/guide/">Vue documentation</a>. As many have said before me, they are incredibly easy to follow; that's part of the reason I got so hooked on this ecosystem. Plus...</p>
<p>I love how <strong>single-file components</strong> compartmentalize the JavaScript and allow you to write CSS right inside the file. The <strong>v-directives</strong> make building components a blast. <strong>Vuex</strong> was immediately much simpler to wrap my head around than Redux, and between <a target="_blank" href="https://devchat.tv/podcasts/views-on-vue/">podcasts</a> and  <a target="_blank" href="https://www.vuemastery.com/">great courses online</a>  to <a target="_blank" href="https://github.com/vuejs/awesome-vue">catch-all repos like this</a>, Vue has a great <strong>community</strong> behind it.</p>
<h2 id="so-lets-build-a-side-project">So let's build a side project 🎉</h2>
<p>I figured when I started this blog that I'd be writing about UI-related topics, not building a side project. But since I use React at work and I've been itching for a reason to get back to Vue, I thought it'd be fun to <strong>document my experience</strong> building with the framework for the first time. So here we are! </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624798869264/XqjNugZKn.jpeg" alt="Artboard – 2.jpg" /></p>
<p>Since I love movies, I'm going to build a movie watch list app called <strong>Medo</strong> (I've never been good at names... 😞) and we'll be working off of the Adobe XD mock-ups pictured above. Some of our MVP requirements for the app will allow users will be able to:</p>
<ul>
<li><strong>Login to their account using mock authorization.</strong> Later on, I'll get to using Firebase.</li>
<li><strong>Query movies from a movie database API.</strong></li>
<li><strong>Manage the movies in their watch list.</strong> This includes organizing them into categories like favorites.</li>
</ul>
<p>In the future, I'd love to make Medo a more social experience so that users can find friends, see what <em>they've</em> been watching, and make recommendations. But this is a start! So, let's wrap up this portion by setting up the project.</p>
<h2 id="project-setup">Project setup ⚒️</h2>
<p>First things first, we'll need to get the Vue CLI installed. The CLI will help us get the app up and running quickly with things like <strong>SCSS</strong>, <strong>Vue Router</strong>, and <strong>Vuex</strong>.</p>
<p>If this is your first time, I would head over to <a target="_blank" href="https://cli.vuejs.org/guide/installation.html">the official documentation</a> and follow the setup guide there. As long as you're familiar with Node and NPM, installation basically boils down to either <code>npm install -g @vue/cli</code> or <code>yarn global add @vue/cli</code>. </p>
<p><strong>Update (6/29/2021)</strong>: I was so excited to publish this that I forgot to tell you a crucial piece...after you install the CLI you'll want to create the app! To make sure the install was successful, quickly run <code>vue --version</code> and be sure that it spits out a version. Then simply running <code>vue create [name-of-app]</code> will set off the configuration process.</p>
<p>From there, I like choosing to manually set the configuration to use Vue 3, Babel, Router, Vuex, CSS Pre-processors, and the Linter / Formatter.</p>
<p>Then, we'll strip down the app so that only two simple views remain. It's important to note that only the template tags are required in <code>.vue</code> files, so the script tags are not necessarily doing anything (yet).</p>
<p>The <strong>Login</strong> view component:</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"login"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Login<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"Login"</span>
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><p>And the <strong>Register</strong> view component:</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">template</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"register"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Register<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">template</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">name</span>: <span class="hljs-string">"Register"</span>
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre><p>We'll also create a couple of SCSS partial files in <code>assets/css</code>, where we're going to be storing variables and mixins. However, our Vue components won't be able to access those partials until we get them configured in a <code>vue.config.js</code> file at the root of our project. The lines we'll need for that can be found below. For more information config settings, I'd recommend - you guessed it -  <a target="_blank" href="https://cli.vuejs.org/config/#vue-config-js">checking out the documentation</a>.</p>
<pre><code><span class="hljs-built_in">module</span>.<span class="hljs-built_in">exports</span> = {
  css: {
    loaderOptions: {
      sass: {
        prependData: `<span class="javascript">
          @<span class="hljs-keyword">import</span> <span class="hljs-string">"@/assets/css/_variables.scss"</span>;
          @<span class="hljs-keyword">import</span> <span class="hljs-string">"@/assets/css/_mixins.scss"</span>;
        </span>`,
      },
    },
  },
}
</code></pre><p>If you want to include formatting with <strong>Prettier</strong>, you can add <code>"plugin:prettier/recommended"</code> to the <code>extends</code> array inside the <code>eslintConfig</code> portion of our <code>package.json</code>:</p>
<pre><code>  <span class="hljs-string">"eslintConfig"</span>: {
    <span class="hljs-string">"extends"</span>: [
      <span class="hljs-comment">// ...existing options</span>
      <span class="hljs-string">"plugin:prettier/recommended"</span>
    ],
  <span class="hljs-comment">// ...rest of the config settings</span>
</code></pre><p>And create a <code>prettier.config.js</code> file to the root of the directory, where you can set all of your formatting options for consistency in your codebase:</p>
<pre><code><span class="hljs-string">module.exports</span> <span class="hljs-string">=</span> {
  <span class="hljs-attr">tabWidth:</span> <span class="hljs-number">2</span>,
  <span class="hljs-attr">semi:</span> <span class="hljs-literal">false</span>,
  <span class="hljs-attr">singleQuote:</span> <span class="hljs-literal">true</span>,
}
</code></pre><p>Now that the the basic setup done, we can move onto the fun part: <strong>building components</strong> 🥳. In my next post, I'll begin doing that by building out the Login page. Meanwhile,  <a target="_blank" href="https://blog.mattpignatore.dev/my-journey-learning-to-code-from-books-and-bootcamp-to-ui-development">check out my last post</a>  where you can get to know me and my journey.</p>
<hr />
<h3 id="you-can-find-me-on-twitter-httpstwittercommattpigs-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on  <a target="_blank" href="https://twitter.com/matt_pigs">Twitter </a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> . Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item><item><title><![CDATA[My Journey Learning to Code: From Books and Bootcamp to UI Development]]></title><description><![CDATA[Hey, I'm Matt 👋 I've been a professional UI Developer for one year (coding for two) and I, like any developer worth their salt, barely know what I'm doing. So, I've been telling myself that I couldn't possibly be capable of blogging and sharing know...]]></description><link>https://blog.mattpignatore.dev/my-journey-learning-to-code-from-books-and-bootcamp-to-ui-development</link><guid isPermaLink="true">https://blog.mattpignatore.dev/my-journey-learning-to-code-from-books-and-bootcamp-to-ui-development</guid><category><![CDATA[UI Design]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[React]]></category><category><![CDATA[PHP]]></category><category><![CDATA[Learning Journey]]></category><dc:creator><![CDATA[Matt Pignatore]]></dc:creator><pubDate>Tue, 22 Jun 2021 12:20:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1624547153468/2lsjboPdg.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hey, I'm Matt 👋 I've been a professional UI Developer for one year (coding for two) and I, like any developer worth their salt, barely know what I'm doing. So, I've been telling myself that I couldn't <em>possibly</em> be capable of blogging and sharing knowledge. What knowledge do I even possess? 🤷‍♂️</p>
<p>Lately, though, I've come to shed that thought and realize that I have experience, and that I'm passionate enough about UI to share my thoughts from time-to-time. So, I've come to the lovely <strong>Hashnode</strong> to put my introverted thoughts to the virtual page.</p>
<p>First, let me tell you a bit about myself and my journey.</p>
<h2 id="about-me-and-everything-i-love">About Me and Everything I Love 🎸</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624362893966/3ceTVUK_p.jpeg" alt="bean 1.jpg" /></p>
<p>My wife's name is <strong>Shannon</strong> and she's the reason I brush my teeth in the morning (that's the saying, right?). She's an elementary school teacher as natural as they come, and way more caring and thoughtful than I'll ever be. We have a dog named <strong>Luna</strong>, a boxer/lab who started graying way too young.</p>
<p>Outside of coding, I love watching and talking about movies and television, and I really enjoy making food for people. Most of all, I grew up passionate about music,  <a target="_blank" href="https://mattpignatore.bandcamp.com/">playing</a>   <a target="_blank" href="https://thehotsweats.bandcamp.com/">in</a>   <a target="_blank" href="https://patmoron.bandcamp.com/album/jawr">bands</a> , and drawn to the process of writing and producing. So much so that I spent my first two years of college studying music production. In the end I went with the "safe" choice that I probably wouldn't make now: <strong>a business degree</strong>. </p>
<h2 id="about-my-journey">About My Journey 🛵</h2>
<p>Three years into my marketing career and I was unhappy and unfulfilled as ever. Commuting to a dull office, wasting gas and physical energy, surrounded by folks talking about improving ROI (whatever <em>that</em> means 😉). Interesting to some, of course, but it was never really my thing.</p>
<p>That's when I bought a book on coding. I studied  <a target="_blank" href="https://www.thriftbooks.com/w/html-and-css-design-and-build-websites_jon-duckett/442192/item/5717183/?gclid=CjwKCAjw8cCGBhB6EiwAgORey936cdeITr-3yst8VngCe7fO8tpkpXfwIdLF9RhxxlKBcaL6R0RtJxoCeCoQAvD_BwE#idiq=5717183&amp;edition=6792839">HTML &amp; CSS by Jon Duckett</a>  front-to-back before seeking out a more structured setting to learn the fundamentals of programming. I signed up for a six-month, full-stack, nights-and-weekends bootcamp soon after, and finished up in February of 2020 while working my 9-to-5. Bootcamps are not for everyone and certainly aren't required, but going to the bootcamp was the <strong>best decision I ever made</strong>. It's a "get out what you put in" sort of thing, and I got two Golden Ducks (see below).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1624362851585/-5wcCMEVK.png" alt="Web 1920 – 1.png" /></p>
<h2 id="post-bootcamp">Post-Bootcamp ⛺</h2>
<p>After graduating I built a couple of small freelance sites, but knew that wasn't the path for me long-term. Ideally, I wanted to build software UI; something that became of interest to me while using (and nitpicking) platforms at my day job.</p>
<p>That opportunity came in August of 2020 when I was hired part-time to re-design the UI of a friend's recruiting software. When I told my peers in bootcamp that I wanted to learn PHP, they laughed. But it came in handy:  <strong>this entire project was built with PHP and jQuery</strong>.</p>
<p>The opportunity buoyed me through the pandemic fall; I was still working my remote day job, and spent a lot of those days (shhh 🤫) reading up on how I was going to tackle a feature, just eager to get back to the UI work. I learned a lot quickly and on the fly.</p>
<p>I was actively looking for my first full-time junior role in March of 2021 when I was asked to interview through  <strong>LinkedIn</strong> for a <em>UI Developer</em> role. A couple of weeks and a few conversations later I got a job offer! <em>Was this real?</em> It was, and I was terrified.</p>
<p>I felt like a fraud. I had never had a proper junior role. It stills feels odd! I couldn't shake the feeling that I missed out on a crucial experience, or that I didn't do it "right" and thus am not as good as others who did. But I've learned to let go of those anxieties as I've gained confidence over the past year. I've gotten comfortable feeling lost, and that's an important feeling for developers to be comfortable with.</p>
<h2 id="my-stack">My "Stack" 🥞</h2>
<p>I like to say my stack is simply <strong>HTML</strong>, <strong>CSS</strong>, and <strong>JavaScript.</strong> I work actively on projects in <strong>React</strong>, <strong>jQuery</strong>, and <strong>Vue</strong>.</p>
<p>I know, I know; that's not a stack. But I really only dabble in the back-end. When I do it's usually in <strong>PHP</strong> and <strong>MySQL</strong>, but I have experience writing back-end code in <strong>Node/Express</strong> and <strong>MongoDB</strong> too.</p>
<hr />
<h3 id="you-can-find-me-on-twitter-httpstwittercommattpignatore-and-githubhttpsgithubcommpaitgt-or-you-can-check-out-my-portfolio-herehttpsmattpignatoredev-feel-free-to-reach-out-id-love-to-hear-from-you">You can find me on  <a target="_blank" href="https://twitter.com/matt_pignatore">Twitter </a> and  <a target="_blank" href="https://github.com/mpaitgt">Github</a> , or you can check out my portfolio  <a target="_blank" href="https://mattpignatore.dev/">here</a> .  Feel free to reach out, I'd love to hear from you!</h3>
]]></content:encoded></item></channel></rss>