# Developing a UI with Vue, Part 2: Project Setup and Design System

In my last post, I  [introduced the UI I'm going to be building](https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project) 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 list app.

You'll want to be familiar with **HTML**, **SCSS**, **Flexbox**, and the basics of **front-end frameworks** (components, state, props, etc.) in order to follow along. If you're not, head over to [freecodecamp.org](https://www.freecodecamp.org/) or something and dive in 🏊 The best way to learn is to build!

But first...

## A basic design system 🎨

Lately I've made it a habit to begin projects by **defining a design system up front**.

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 `10px` and `12px` of padding, or between `#this` or `#that` shade of blue.

I came across this tip in a really great e-book called [Refactoring UI](https://www.refactoringui.com/), written by **Adam Wathan** and **Steve Schoger** of Tailwind fame.

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.

> A simple approach is to start with a sensible base value, then build a scale using factors and multiples of that value.

> 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.

> 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.

---

You can check out the design system I'm using for **Medo** through the [Github repo](https://github.com/mpaitgt/medo-app) I have set up, but it generally looks something like this:

```scss
// color
$white: #ffffff;
$blue100: #f0f5ff;
$blue200: #c9ddff;
$gray100: #d6d6d6;
$gray200: #7c89a1;

// space 
$space1: 0.4rem;
$space2: 0.8rem;
$space3: 1.2rem;
$space4: 1.6rem;

// text size
$text1: 1.2rem;
$text2: 1.4rem;
$text3: 1.6rem;
$text4: 1.8rem;

// gradients
$bg: linear-gradient(180deg, white, $blue150);
```

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:

```scss
@mixin flex(
  $direction: row, 
  $justify: flex-start, 
  $align: flex-start, 
  $grow: 0
) {
  display: flex;
  flex-direction: $direction;
  justify-content: $justify;
  align-items: $align;
  flex-grow: $grow;
}

// example use case
.some-flex-container {
  @include flex(row, justify-between, center);
}
```

Mixins are also great for things like [media queries](https://css-tricks.com/approaches-media-queries-sass/) too! 

I've been re-using these really simple design system settings in several projects recently and have found them very valuable! Highly recommended.

---

## My First Mobile-First App 📱

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 *exclusively* for mobile. Let's make that happen with a little bit of (S)CSS:

```scss
body {
  background: #a7cdff;
  font-size: 2.0rem;
  font-family: Lane, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased; // default preset with Vue CLI 
  -moz-osx-font-smoothing: grayscale; // default preset with Vue CLI
}
#app {
  height: 100vh;
  width: 100%;
  max-width: 428px;
  margin: 0 auto;
  background-color: $bg;
  background-attachment: fixed;
  box-shadow: 0px 0px 10px -2px rgba(0,0,0,0.16);
  position: relative;
  overflow: scroll;
}
```
We’ll also set up a base template for our app in `App.vue`, which will house our `<nav>` element containing `<router-link>` tags, as well as the `<router-view />`.

```html
<template>
  <nav id="nav" role="navigation" aria-label="Main Navigation">
    <ul>
      <li>
        <router-link to="/">Login</router-link>
      </li>
      <li>
        <router-link to="/register">Register</router-link>
      </li>
    </ul>
  </nav>
  <router-view />
</template>

```

We'll finish the nav off for now by extracting it to its own component called `Navigation.vue` in `src/components/layout`.

If the above route tags look unfamiliar to you, they're likely your introduction to [Vue Router](https://router.vuejs.org/). 

## Routing with Vue 🛣️

Vue Router, like **React Router**, handles all of the users' URL requests for new pages and sub-pages. The `<router-view />` you see in the template above is the window into each of our app's different pages/views, and the `<router-link>` tags are just anchor tags made special. 

Rather than making a **server request** 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 **rendering components** instead; no page loads or server requests required.

In order to define our **Medo** routes, we utilize the `index.js` file in `router/` and import our views. **Since we selected to use Vue Router when we used Vue CLI for setup, the router has come already configured.** All we need to do is import our own views, `Login.vue` and `Register.vue`, and define them in the routes array:

```javascript
import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'

const routes = [
  {
    path: '/',
    name: 'Login',
    component: Login,
  },
  {
    path: '/register',
    name: 'Register',
    component: Register,
  },
]

const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes,
})

export default router

```

**Note**: I'm making the root of our app, `/`, 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.

## The Flex Footer 🦶

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 *not* logged in. We're going to place that in the `App.vue` template just below the router, with a couple of changes to our HTML mark-up:

```html
<template>
  <div class="page-wrapper">
    <Navigation />
    <div class="page">
      <router-view />
    </div>
    <footer role="navigation" aria-label="Footer Navigation">
      <img src="./assets/logo.png" alt="Medo logo" width="76" />
      <nav>
        <ul>
          <li>About</li>
          <li>Contact</li>
        </ul>
        <ul>
          <li>Login</li>
          <li>Register</li>
        </ul>
      </nav>
      <div class="copyright">
        <p>&copy; 2021, Medo</p>
      </div>
    </footer>
  </div>
</template>
```

We're doing a few things here:

1. **Wrapping the entire template in a div with class "page-container".** This allows us to apply Flexbox to the app container, which has a `min-height: 100vh`.
2. **Wrapping the `<router-view>` element in a div with class "page".** This class sets `flex-grow: 1`, pushing the footer to the bottom (as long as Step 1 is applied) no matter what content is placed here.
3. **Adding our footer markup.** This includes an app logo, future navigation items, and a copyright tag.

Like we did with Navigation.vue, we'll extract the footer to its own component, so that the `App.vue` template looks like this:

```html
<template>
  <div class="page-wrapper">
    <Navigation />
    <div class="page">
      <router-view />
    </div>
    <Footer />
  </div>
</template>
```

The script tag looks like this:

```html
<script>
import Navigation from '@/components/layout/Navigation'
import Footer from '@/components/layout/Footer'

export default {
  name: 'App',
  components: {
    Navigation,
    Footer,
  },
}
</script>
```

And the style tag looks like this:

```scss
<style lang="scss">
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
html {
  font-size: 62.5%;
}
body {
  font-size: 2.0rem;
  font-family: Lane, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  background: #a7cdff;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 100;
}
p {
  line-height: 1.5;
}
ul {
  list-style: none;
  margin: 0;
}
#app {
  background: $bg;
  background-attachment: fixed;
  width: 100%;
  max-width: 428px;
  height: 100vh;
  margin: 0 auto;
  position: relative;
  box-shadow: 0px 0px 10px -2px rgba(0,0,0,0.16);
  overflow: scroll;
}
/* Hide scrollbarfor Chrome, Safari and Opera */
#app::-webkit-scrollbar {
  display: none;
}
/* Hide scrollbar for IE, Edge and Firefox */
#app {
  -ms-overflow-style: none;  /* IE and Edge */
  scrollbar-width: none;  /* Firefox */
}
.page-wrapper {
  @include flex(column);
  min-height: 100vh;
}
.page {
  flex-grow: 1;
}
</style>

```

**And we're finished!**

In my next post, I'll be focused on developing **mobile navigation** and introducing state into our application for the first time. In the meantime, check out  [what led me to choose Vue](https://blog.mattpignatore.dev/developing-a-ui-with-vue-part-1-introducing-the-project).

---

### You can find me on  [Twitter ](https://twitter.com/matt_pigs) and  [Github](https://github.com/mpaitgt) , or you can check out my portfolio  [here](https://mattpignatore.dev/) . Feel free to reach out, I'd love to hear from you!




