Ivan Reyné

Vue.js and Vuetify quick prototypes - all in one file

Vue.js and Vuetify quick prototypes - All in one file

In this post we are going to explain how you can quickly create a website/app prototype with Vue.js and Vuetify.

You can find the full code at GitHub and also at CodePen.

Motivation

Have you ever had an idea for an application but you were not sure if it would work? Did you ever had to start a project with a client with few and unclear specs?!? :-D Do you usually have a hard time matching what the designers "paint" with what the application ends up looking like? ... Well ... then you are in the right place! Creating an application or website prototype with Vue.js and Vuetify is really easy. You will be able to implement the basic navigation. The designers can also check if what they are thinking works. You can share it with your clients directly or upload it to a test web site easily with no back-ends.

And the best thing is that as Vuetify is a Material Design implementation you can use this approach to create your prototype even if you end up using a different tool chain.

Interested? ... :-)

How?

We are going to start with the "all in one" approach. This means that we will have all our application/website in one single HTML file. Now, this will work really well for smallish applications/websites but for bigger ones we will need a slightly different approach (we will see that later). The main advantage is that you can load the prototype directly on the browser (file -> open) without needing to deploy it to any web server. And this is quite a big bonus!

As Vue.js is a "progressive web framework" that you can load and use straight from a content delivery network, and the same applies for Vuetify, we will make good use of that to build the prototype in a single HTML file. Basically we will:

  • Load the Vue.js and Vuetify frameworks from content delivery networks.
  • Define each "page" of the application as separate DIVs.
  • Instantiate Vuetify with a proper theme and colours.
  • Configure the navigation routes (with the Vue.js router) to match as closely as you can the real application navigation.
  • Design your pages (we are not going to go into detail on this point in this tutorial as we centre on how to build the skeleton functionality)
  • Relax and drink a cup of tea (or beer).

It sounds complicated but it really is not, bear with me.

It would be nice if you have a bit of an idea of how Vue.js works. If not don't worry because the creation of prototypes is so simple that with a basic knowledge of JavaScript syntax you will understand what is going on.

Plan (some of) it upfront

The first step is to know where you are going. If you don't know where you want to go then any road will do and that is not what we want.

In this case as the purpose is a bit of demonstration we will do a simple application with four pages:

  • One home dashboard with various cards.
  • A task list.
  • A task detail view.
  • One about/help page.

Moreover the application skeleton will have a drawer menu. There will be a system bar with notifications and user profile information. And finally we will have a simple application footer.

It will be blueish ... with a hint of orange!

It might look like a pretty simple list of requirements but we are sure that more than one reader has started a real project with fewer specifications than that ... are we wrong?!? ... We are not! ... sadly! :-D

Prepare the HTML file

You need 1 HTML file, so let's create a very basic HTML file, nothing more needed.

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
</body>

Load the Vue.js and Vuetify frameworks

Now let's add the Vue.js, vue-router, Vuetify and fonts needed:

<!DOCTYPE html>
<html>
<head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.materialdesignicons.com/4.4.95/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
</head>
<body>
    <div id="app">
    </div>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
    <script>
    new Vue(
        {
            el: '#app'
        }
    );
    </script>
</body>

Believe it or not, if you are not familiar with Vue.js, now you have the framework (plus a Material Design component library) loaded and ready!

Define each "page" of the application

This is the key point of the process: as we are "packing" an entire single page application (SPA) into a single HTML page (without compiling or anything) we need a way to easily define each page/component. We will use hidden DIVs as a way to store the templates of the components.

First we define a custom style to hide the DIVs:

    <style>
    .vue-component-def { display: none; }
    </style>

As we know the amount of pages we need we can define each as a DIV with its proper id. Later on we will have a JavaScript function that looks for this DIVs and reads their "innerHTML" in order to create the Vue.js templates for the components.

    <div id="app">
    </div>
    ...
    <div id="home" class="vue-component-def">
        ...
    </div>

    <div id="tasks" class="vue-component-def">
        ...
    </div>

    <div id="tasks-detail" class="vue-component-def">
        ...
    </div>

    <div id="about" class="vue-component-def">
        ...
    </div>

Obviously you need some content for those pages. You will see the content on the final part and also on the GitHub and CodePen repositories. Now we are centering on how you need to organise the "working parts" of the prototype so you can extend it easily.

Instantiate Vuetify

One of the things "TO-DO" is to instantiate Vuetify and to load the component framework. As Vue.js is designed to be a "progressive framework" you can load on the spot this is pretty easy:

    <script>
    new Vue(
        {
            el: '#app',
            vuetify: new Vuetify({
                theme: {
                    options: {
                        customProperties: true,
                    },
                    themes: {
                        light: {
                            primary: '#4dd0e1',
                            secondary: '#ffb300',
                            accent: '#ff5722',
                            error: '#f44336',
                            warning: '#ffc107',
                            info: '#03a9f4',
                            success: '#4caf50'
                        },
                    },
                },
                icons: {
                    iconfont: 'mdi',
                }
            }),
            ...
        }
    );
    </script>

Configure the navigation routes (with the Vue.js router) to match as closely as you can the real application navigation.

The last, but quite important point, is to define the single page application routes. We need, not only, to define the router of the application but also to assign them a Vue.js component ... and to create that component. If you work with Vue.js you know that usually you define each component as a separate file. In this case we aim to define everything in a single HTML file ... how? Well, quite simple a file component is nothing more than a JavaScript object with a set of defined keys. You can find all the information on the Vue.js documentation. So we will simply create a custom component that will contain only the template that is the "html" defined on the DIVS we have defined for each page:

    <script>
    new Vue(
        {
            el: '#app',
            ...
            router: new VueRouter({
                routes: [
                    { path: '/home', component: { template: document.querySelector('#home').innerHTML } },
                    { path: '/tasks', component: { template: document.querySelector('#tasks').innerHTML } },
                    { path: '/tasks/detail', component: { template: document.querySelector('#tasks-detail').innerHTML } },
                    { path: '/about', component: { template: document.querySelector('#about').innerHTML } },
                    { path: '*', redirect: '/home' }
                ]
            }),
            ...
        }
    );
    </script>

This works!

Design your pages

As said above we are not going to go into detail on how the design should be done as we are centering the article on providing you with the skeleton you need to test different designs. Besides, each application is different and your clients will want it in specific way. Still we will provide a full example with a sample layout and components.

Because we are using Vuetify as base for the design and layout it is quite easy to put together a design simply following their documentation. We are not going to have "almost any" functionality so everything will be Vuetify HTML components.

If you look at the sample application you will notice that we have organised it so the "v-app" contains a basic page layout with "v-navigation-drawer", "v-app-bar" and a "v-footer". Then inside we have the "router-view" that will change the contents of the main area as defined in our routes and components. This had the limitation that we can only have "one" application layout ... unless you want to have the main "v-app" with simple the "router-view" and then repeat the navigation drawers and other common layout components for each page. We will write another article on how to create a more complex prototype with different layouts were you can re-use components.

The other thing that is important is to see that to change route we are using a standard "to=route". And finally you can see that you can have a bit of JavaScript embedded inside the HTML templates and evaluated when the component is created, look at the image src attribute:

<v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">

This could become quite useful in case you need something a bit more advanced for your prototyping.

We have used standard Vuetify components, some Lorem Ipsum, some Lorem Ipsum for images ... nothing really fancy as the intention of the article is showing you the possibilities. Still you can see that in a moment you will have a decent looking Material Design application prototype up, running and ready to be sent to your costumers for approval.

You can find the full code in the next section, on CodePen and on GitHub.

Enjoy!

The full thing

This needs a bit of work on your part because you are going to have to understand how Vuetify works an defines its components. Just check their documentation.

<!DOCTYPE html>
<html>
<head>
    <link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
    <link href="https://cdn.materialdesignicons.com/4.4.95/css/materialdesignicons.min.css" rel="stylesheet">
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, minimal-ui">
    <style>
    .vue-component-def { display: none; }
    </style>
</head>
<body>

    <div id="app">
        <v-app>
            <v-navigation-drawer app fixed v-model="drawer">
                <v-list dense nav>

                    <v-list-item to="/home">
                        <v-list-item-action>
                            <v-icon>mdi-home</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Home</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item to="/tasks">
                        <v-list-item-action>
                            <v-icon>mdi-calendar-check</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Tasks</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item to="/about">
                        <v-list-item-action>
                            <v-icon>mdi-help-circle</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>About</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                    <v-list-item>
                        <v-list-item-action>
                            <v-icon>mdi-logout</v-icon>
                        </v-list-item-action>
                        <v-list-item-content>
                            <v-list-item-title>Sign Out</v-list-item-title>
                        </v-list-item-content>
                    </v-list-item>

                </v-list>
            </v-navigation-drawer>

            <v-app-bar app fixed color="primary">
                <v-app-bar-nav-icon @click.stop="drawer = !drawer"></v-app-bar-nav-icon>

                <v-toolbar-title>Vuetify Prototype</v-toolbar-title>

                <div class="flex-grow-1"></div>

                <v-btn icon>
                    <v-badge color="accent" overlap>
                        <template v-slot:badge>4</template>
                        <v-icon>mdi-bell</v-icon>
                    </v-badge>
                </v-btn>

                <v-btn icon>
                    <v-badge color="accent" overlap>
                        <template v-slot:badge>3</template>
                        <v-icon>mdi-email</v-icon>
                    </v-badge>
                </v-btn>

                <div class="d-flex">
                    <v-avatar>
                        <img :src="'https://stevensegallery.com/100/100/?rand=' + Math.random()" />
                    </v-avatar>
                    <div class="d-none d-sm-flex flex-row align-center mx-2">
                        <div class="flex-column">
                            <div class="body-1">Steven Seagal</div>
                            <div class="body-2 font-weight-light">Head of Security</div>
                        </div>
                    </div>
                </div>
            </v-app-bar>

            <v-content>
                <router-view></router-view>
            </v-content>

            <v-footer app inset color="primary" height="auto">
                <v-container fluid>
                    <v-row align="start" justify="center" no-gutters>
                        <v-col cols="12">
                            <div class="text-center">
                                <a class="black--text" href="https://ivanreyne.ninja">Made by Ivan Reyné, enjoy!</a>
                            </div>
                        </v-col>
                    </v-row>
                </v-container>
            </v-footer>
        </v-app>
    </div>

    <div id="home" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">
                    <h1 class="text-center">Home Dashboard</h1>
                </v-col>
            </v-row>
            <v-row align="start" justify="start">
                <v-col md="3">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-card-text>

                        <v-card-actions>
                            <v-btn>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
                <v-col md="6">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/400/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn color="accent" outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-email-outline</v-icon>
                            </v-btn>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
                <v-col md="3">

                    <v-card class="mx-auto">
                        <v-card-title>
                            <p class="headline">Card List</p>
                        </v-card-title>

                        <v-list two-line>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 1</v-list-item-title>
                                    <v-list-item-subtitle>Item 1 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="accent">mdi-star</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 2</v-list-item-title>
                                    <v-list-item-subtitle>Item 2 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="accent">mdi-star-half</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item>
                                <v-list-item-content>
                                    <v-list-item-title>Item 3</v-list-item-title>
                                    <v-list-item-subtitle>Item 3 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-list-item-subtitle>
                                </v-list-item-content>

                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                        </v-list>

                        <v-card-actions>
                            <v-btn color="accent" outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-email-outline</v-icon>
                            </v-btn>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
            <v-row align="start" justify="start">
                <v-col md="6">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus.</v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="tasks" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>
                            Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.
                        </v-card-text>

                        <v-list two-line>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 1</v-list-item-title>
                                    <v-list-item-subtitle>Item 1 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 2</v-list-item-title>
                                    <v-list-item-subtitle>Item 2 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>
                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                            <v-list-item to="/tasks/detail">
                                <v-list-item-content>
                                    <v-list-item-title>Item 3</v-list-item-title>
                                    <v-list-item-subtitle>Item 3 description - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim.</v-list-item-subtitle>
                                </v-list-item-content>

                                <v-list-item-action>
                                    <v-btn icon>
                                        <v-icon color="grey lighten-1">mdi-star-outline</v-icon>
                                    </v-btn>
                                </v-list-item-action>
                            </v-list-item>
                        </v-list>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="tasks-detail" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="100px" :src="'https://placeimg.com/600/100/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Task detail</v-card-title>
                        </v-img>

                        <v-card-text>
                            <p>Task detail - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</p>
                            <p>Nunc in erat at nisl mattis sagittis. Sed viverra ipsum quis nisl venenatis, non tincidunt lectus viverra. Phasellus placerat molestie pellentesque. Fusce non enim eleifend, sagittis risus a, cursus ante. Donec sagittis lobortis magna sed imperdiet. Suspendisse ultrices tellus id ex vehicula, eu mattis nisl vulputate. Maecenas quis sapien nibh. Quisque nec eros nibh. Mauris accumsan finibus quam, eget viverra magna molestie et. Suspendisse accumsan porta mi. Morbi eu dui euismod turpis ultrices placerat vitae ac magna. Quisque elementum id quam vel viverra. Fusce sit amet nunc nec nunc dignissim semper. Proin imperdiet, felis vitae aliquam vulputate, tortor nisi feugiat eros, eget molestie mi mi nec dui. Sed vel risus sit amet mi pretium dictum id ac mauris. Morbi vitae quam eget nulla semper lacinia.</p>
                        </v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <div id="about" class="vue-component-def">
        <v-container fluid>
            <v-row align="start" justify="center">
                <v-col cols="12">

                    <v-card class="mx-auto">
                        <v-img class="white--text" height="200px" :src="'https://placeimg.com/600/200/nature?rand=' + Math.random()">
                            <v-card-title class="align-end fill-height">Lorem ipsum dolor sit amet</v-card-title>
                        </v-img>

                        <v-card-text>
                            <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas eleifend elit quis lorem malesuada lobortis. Cras et est nec libero venenatis cursus. Sed lacinia ut arcu eget dignissim. Phasellus eget dapibus mauris. Duis et interdum ex, eu pellentesque lectus. Phasellus gravida ligula in purus tincidunt vehicula nec sit amet orci. Donec et auctor sapien. Cras lobortis ante nisl, non faucibus sem luctus id. Quisque tristique ante eget pharetra posuere.</p>
                            <p>Nunc in erat at nisl mattis sagittis. Sed viverra ipsum quis nisl venenatis, non tincidunt lectus viverra. Phasellus placerat molestie pellentesque. Fusce non enim eleifend, sagittis risus a, cursus ante. Donec sagittis lobortis magna sed imperdiet. Suspendisse ultrices tellus id ex vehicula, eu mattis nisl vulputate. Maecenas quis sapien nibh. Quisque nec eros nibh. Mauris accumsan finibus quam, eget viverra magna molestie et. Suspendisse accumsan porta mi. Morbi eu dui euismod turpis ultrices placerat vitae ac magna. Quisque elementum id quam vel viverra. Fusce sit amet nunc nec nunc dignissim semper. Proin imperdiet, felis vitae aliquam vulputate, tortor nisi feugiat eros, eget molestie mi mi nec dui. Sed vel risus sit amet mi pretium dictum id ac mauris. Morbi vitae quam eget nulla semper lacinia.</p>
                        </v-card-text>

                        <v-card-actions>
                            <v-btn color="primary">Action</v-btn>
                            <v-btn outlined>Action</v-btn>
                            <div class="flex-grow-1"></div>
                            <v-btn icon>
                                <v-icon>mdi-heart-outline</v-icon>
                            </v-btn>
                        </v-card-actions>
                    </v-card>

                </v-col>
            </v-row>
        </v-container>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue-router.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
    <script>
    new Vue(
        {
            el: '#app',
            vuetify: new Vuetify({
                theme: {
                    options: {
                        customProperties: true,
                    },
                    themes: {
                        light: {
                            primary: '#4dd0e1',
                            secondary: '#ffb300',
                            accent: '#ff5722',
                            error: '#f44336',
                            warning: '#ffc107',
                            info: '#03a9f4',
                            success: '#4caf50'
                        },
                    },
                },
                icons: {
                    iconfont: 'mdi',
                }
            }),
            router: new VueRouter({
                routes: [
                    { path: '/home', component: { template: document.querySelector('#home').innerHTML } },
                    { path: '/tasks', component: { template: document.querySelector('#tasks').innerHTML } },
                    { path: '/tasks/detail', component: { template: document.querySelector('#tasks-detail').innerHTML } },
                    { path: '/about', component: { template: document.querySelector('#about').innerHTML } },
                    { path: '*', redirect: '/home' }
                ]
            }),
            data: {
                drawer: null
            }
        }
    );
    </script>
</body>
</html>

Relax and drink a cup of tea (or beer).

That was about time! :-)