

Vue & Tailwind web template23/11/2023
Pre-requisites
This tutorial covers how to use Tailwind and install it on your Vue app. I recommend referring to my tutorial on creating a Vue app if this is your first time working with Vue. If you're struggling with understanding certain concepts covered in this tutorial, don't worry, you can refer to the source code of the template being built.
This tutorial will be using npx in order to install Tailwind CSS in the app, run this command to install npx.
npm install -g npx
Installing Tailwind
In this tutorial, I will be installing Tailwind with Vite, these are the commands provided in their documentation to enter into the terminal.
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Then add this code snippet to the top of the style.css
@tailwind base;
@tailwind components;
@tailwind utilities;
After installing Tailwind, you will notice a new file called tailwind.config.js. In order to use Tailwind in our project files, we need to add the following code into the content array:
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
Now you can use Tailwind classes anywhere across your app. Tailwind is a utility-first CSS framework which may take some time getting used to especially transitioning from Bootstrap. I always refer to Tailwind's documentation which has very good search functionality to find the CSS properties / values you want to use.
Theme configuration
In the tailwind.config.js file you can configure the theme colour palatte, fonts and many more. Refer to Tailwind Theme Configuration documentation for more detail. I have provided an example which I use in my template below.
First I want to configure the colour scheme so I can use class names like "text-blue" and "bg-blue". We achieve this by adding a "colors" object inside the "theme" object listing the name and hex value of the colours.
theme: {
colors: {
'hibiscus': '#b94951',
'toadstool': '#b82731',
'stizza': '#870b0c',
'dark': '#111',
'stone': '#3D4351',
'grey': '#999',
'white': '#fff'
}
}
Next I want to be able to use a class name that applies a background image that I have made for the template. This is configurable in the "extend" object under "backgroundImage". These background images are now available as class names called "bg-footer-lace" and "bg-header-pattern".
theme: {
extend: {
backgroundImage: {
'footer-lace': "url('/lace.png')",
'header-pattern': "url('/mask.png')",
}
}
}
Here is the finished result of the Tailwind config file with all the theme settings.
Component structure
In a web app, we break down different elements and functionality into separate files called components. Imagine them as lego bricks. It's recommended to group related components into folders to make it easier to develop / maintain your app and reduce the amount of duplicated code (which reduces the bundle too).
Here is my list of components I created for the template:
Header Folder: Header.vue, Logo.vue, Navigation.vue, NavigationLink.vue
Footer Folder: Footer.vue, Widget.vue, Affiliates.vue, Social.vue, SocialLink.vue
PatternHeading.vue
Vite provides an example component called HelloWorld.vue which shows that each component is broken down into three sections: script, template and style. This is called a Single-File component where all the JavaScript, HTML and CSS of a component is contained in one file and makes your code a lot more modular and easier to maintain.
Template layout
Now, finally, time to build!
The App.vue file will be the skeleton of the template and contain the header, footer and content. You need to import your components inside the <script> section.
<script setup>
import Header from './components/Header/Header.vue'
import Logo from './components/Header/Logo.vue'
import Navigation from './components/Header/Navigation.vue'
import Footer from './components/Footer/Footer.vue'
</script>
Here's the code added to the <template> section
<template>
<div class="site-container flex flex-col grow">
<div class="flex">
<Header />
<div class="bg-stizza flex flex-col justify-between w-full max-w-[21.25rem]">
<Logo />
<Navigation />
</div>
</div>
<div class="bg-white px-4 lg:px-6 pt-12 pb-24 bg-footer-lace bg-bottom bg-repeat-x grow">
<h1>Inori web template</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin efficitur elit et sapien accumsan faucibus. Pellentesque urna nibh, imperdiet condimentum semper eu, facilisis vitae enim. Vestibulum viverra vehicula fermentum. Mauris a porta orci. Cras luctus purus a eleifend rutrum. Duis porttitor ullamcorper magna. Duis vitae aliquam velit. Quisque sollicitudin felis in sagittis tristique. Nulla in consequat tortor.</p>
</div>
<Footer />
</div>
</template>
The <template> section starts off with a div which will have a custom class name of "site-container" and will use Tailwind classes of flex, flex-col and grow. This is a quick way of adding styling of:
display: flex;
flex-direction: column;
flex-grow: 1;
To style the sidebar containing the Logo and Navigation, I am using one of the colours configured in my Tailwind config file. I have set the background colour of the sidebar as red using "bg-stizza". You don't have to define everything in the config though. Using square brackets, I have set the max-width of the sidebar to 21.25rem using "max-w-[21.25rem]"
You can also control styling for different breakpoints with class names too. My content area is using a class name of "lg:px-6" which translates to:
@media (min-width: 1024px) {
padding-left: 1.5rem;
padding-right: 1.5rem;
}
You can change the default breakpoints in the Tailwind config. Personally I don't see anything wrong with the default breakpoints Tailwind has provided but you might want to add an extra breakpoint to handle wider displays like 1600px.
Header component
Here is the code for the Header.vue component below. Notice that even though I don't have any JavaScript needed for this component, I still have an empty <script setup> tag in order to define it as a component.
<script setup>
</script>
<template>
<header id="header" class="h-[32rem] border-b-4 border-stizza">
<img src="/header.png" class="h-full object-cover" role="presentation" />
</header>
</template>
Logo component
This is the logo component code. Nothing new to report here, it's a simple component.
<script setup>
</script>
<template>
<div class="flex justify-center p-4">
<img src="/logo.png" alt="Vizune" />
</div>
</template>
Navigation component
In the navigation component, I am using JSON and v-for to help generate a list of links rather than duplicating code. It translate as "For each object in nav, render the NavigationLink component and send the information to that component too".
<script setup>
import NavigationLink from './NavigationLink.vue'
const nav = [
{
page: "Blog",
link: "/",
active: false
},
{
page: "About",
link: "/about",
active: false
},
{
page: "Avatars",
link: "/avatars",
active: true
},
{
page: "Renders",
link: "/renders",
active: false
},
{
page: "Templates",
link: "/templates",
active: false
},
{
page: "Tutorials",
link: "/tutorials",
active: false
},
{
page: "PSD Downloads",
link: "/psd",
active: false
}
]
</script>
<template>
<nav id="navigation" class="border-b-4 border-stizza">
<ul class="flex flex-col list-none p-0">
<NavigationLink v-for="item in nav" :page="item.page" :link="item.link" :active="item.active" />
</ul>
</nav>
</template>
Navigation link component
The NavigationLink component is using props which is information we are passing through to use in the template. I am controlling the hover state of the link by using hover: in Tailwind.
You can dynamically change a class name in Vue using class binding. In this component, if the "active" prop receives "true" from the Navigation component then we add the class name "bg-dark". If it's false, we use the "bg-stizza" class name. I am using a ternary operator which is shorthand for if/else in JavaScript.
<script setup>
const props = defineProps({
page: String,
link: String,
active: Boolean
})
</script>
<template>
<li class="text-left">
<a
:href="link"
class="hover:bg-white text-white focus:bg-white hover:text-stizza block px-8 py-4"
:class="active ? 'bg-dark' : 'bg-stizza'">
<span class="uppercase tracking-wider text-sm md:text-lg whitespace-nowrap">{{ page }}</span>
</a>
</li>
</template>
Footer component
This is another simple component that handles the layout of the footer layout and I am importing components with the columns of content. The Social component will be explained in more detail in another tutorial in the future.
<script setup>
import Widget from './Widget.vue';
import Social from './Social.vue';
import Affiliates from './Affiliates.vue';
</script>
<template>
<footer class="bg-dark w-full grid grid-cols-1 md:grid-cols-2 border-b-4 border-toadstool">
<Widget heading="Affiliates">
<Affiliates />
</Widget>
<Widget heading="Social">
<Social />
</Widget>
</footer>
</template>
Widget component
This component is using Vue Slots which allows you to inject content into a component. You can see in the Footer component above where to Affiliates and Social component will be placed where the <slot /> is located in the widget.
<script setup>
import PatternHeading from '../PatternHeading.vue';
const props = defineProps({
heading: String,
})
</script>
<template>
<div class="w-full">
<PatternHeading class="text-center text-xxl font-bold text-white mb-5">{{ heading }}</PatternHeading>
<div class="px-6 pb-6">
<slot />
</div>
</div>
</template>
Pattern heading component
You don't have to use Tailwind for everything because it can become, what developers like to call, "Class Soup" and it can make your code regressive because it's harder to maintain.
This is the reason why I prefer to still style :before and :after in old skool CSS. If you disagree (which is fair) then refer to Tailwind documentation of :before & :after.
The scoped attribute on the <style> tag ensures that the CSS only applies to the component and not across the app.
<template>
<h2 class="text-white bg-dark mb-1 p-3 relative z-10 before:bg-header-pattern">
<span class="font-medium uppercase text-xl tracking-wider">
<slot />
</span>
</h2>
</template>
<style scoped>
h2:before {
content: '';
display: block;
width: 100%;
height: 100%;
opacity: 0.5;
position: absolute;
top: 0;
left: 0;
z-index: -1;
}
</style>
Well, that was a lengthy tutorial and this only covers the main elements of the templates. I'm planning on writing more Vue tutorials which will cover complex components like tabs and other aspects of a web app like Routing. Any feedback will be greatly appreciated and let me know what coding tutorials you would like to see in the future. See ya~

