# VuePress Tutorial 8 - Custom Footer
# What is a Footer?
Now it's time to start building the custom footer component for the site. Before creating the footer component, we're going to first describe what a footer is then we'll mention some other options for footers provided by VuePress (opens new window) and the @vuepress/theme-blog (opens new window).
A footer is located at the bottom of a page, and it typically contains:
- Authorship Information
- Copyright Information
- Contact Information
- Sitemap (Important Links Regardless of Current Page - Similar to Global Navbar)
For the Code Monkeys Blog we'll be building a footer that consists of links to various social media platforms. This gives the user an easy way to interact with various content and the community.
Take a look at the footer on this page to see what we'll be designing for the blog. To see another example you can check out the VuePress (opens new window) site footer as well.
Make sure you start the local development server which should be running at http://localhost:8080/ (opens new window) to see the changes we'll be making to the site. If the changes aren't appearing after you save them, then try restarting your local development server.
# Homepage Layout Footer
If you remember from the previous post VuePress Tutorial 6 - Homepage Layout, VuePress (opens new window) provides a way to add a footer to the homepage by adding the following to the homepage layout which is located in docs/README.md
:
If you do decide to add a footer using the frontmatter in the homepage, then the HTML will look like this:
# Rich-text Footer
VuePress (opens new window) also provides a rich-text footer (opens new window) which gives you the ability to easily add more functionality to your footer like links. To set this type of footer you need to use markdown slot syntax (opens new window) which we won't discuss in detail here.
Since the rich-text footer (opens new window) uses markdown slot syntax (opens new window), you can only add it to markdown files, and it needs to be manaully added to each markdown file to be displayed.
These limitations are why we'll be creating a custom footer component. If you only want to show the footer on one or a few markdown pages, then this option should work fine for you.
# VuePress Blog Theme Footer
The Code Monkeys Blog uses the default theme (opens new window), but if you're interested in using @vuepress/theme-blog (opens new window) be sure to take a look at the footer option (opens new window).
Here are some examples of sites that use the @vuepress/theme-blog (opens new window) footer:
Since we'll be using social media sites that are not currently supported by the footer.contact (opens new window) option, we won't be using this footer. You can contribute social media contact types by making a pull request to the vuepress-theme-blog (opens new window) repository though if you're interested.
# Custom Footer
Before designing the custom footer component, we need to have an understanding of the following topics:
- Writing a Theme (opens new window)
- Plugins (opens new window)
- vuepress-plugin-svg-icons (opens new window)
- Theme Inheritance (opens new window)
- globalLayout (opens new window)
We won't be describing every detail about each topic above since we only need to understand how to create our custom footer component. If you're interested in learning more now, then check out the links above.
# Writing a Theme
In VuePress (opens new window) you have the ability to make your site into a theme. This gives you the option to publish your site as an npm (opens new window) package which allows other developers to easily install and use your theme.
To write your own theme you need to create a theme
directory in the .vuepress
directory. The docs
directory for your site should now look something like this:
.
├── docs
│ ├── .vuepress
│ │ ├── public
│ │ ├── theme
│ │ └── config.js
│ └── README.md
After creating the theme
directory, all you need to do is create a Layout.vue
file inside of it like this:
.
├── docs
│ ├── .vuepress
│ │ ├── public
│ │ ├── theme
│ │ │ └── Layout.vue
│ │ └── config.js
│ └── README.md
From here you can develop your site like any other Vue (opens new window) application by organizing your theme however you want. Being able to organize your theme however you want provides a lot of flexibility when creating your site, but it's recommended to use the directory structure below when designing your theme:
theme
├── components
│ └── xxx.vue
├── global-components
│ └── xxx.vue
├── layouts
│ ├── Layout.vue (Mandatory)
│ └── xxx.vue
├── styles
│ ├── index.styl
│ └── palette.styl
├── templates
│ ├── dev.html
│ └── ssr.html
├── enhanceApp.js
└── index.js
Here's a description for each directory and file in the theme
directory:
components
: Local components used in your theme.global-components
: Components in this directory automatically get registered as global, so you don't need to explicitly import them in a file when using them.layouts
: Layout components used in your theme.Layout.vue
: A mandatory layout file for every theme.
styles
: Stores files related to styling your theme.index.styl
: Overrides any default styling and allows you to globally style your site.palette.styl
: Overrides any default styling variables and allows you to add any global styling variables.
templates
: Stores HTML template files.dev.html
: HTML template file for development environment.ssr.html
: HTML template file used in the build time.
enhanceApp.js
: Enhances the theme of your site by giving you the ability to install Vue plugins, add router hooks, etc.index.js
: Entry file for for theme configuration.
Reviewing the Recommended Directory Structure for Themes
You may have recognized this directory structure from the VuePress Tutorial 4 - Directory Structure post.
When creating the custom footer component we'll use the global-components
directory, the index.js
file, and the layouts
directory. The global-components
directory will be where we add the custom footer component since we want the footer to be available globally. The index.js
file will be used to inherit the default theme (opens new window) using theme inheritance (opens new window). Finally, the layouts
directory will be where we add the GlobalLayout.vue
file which allows us to add our custom footer component to the global layout of the site.
Since we'll be using the vuepress-plugin-svg-icons (opens new window) to add social media icons to our footer, let's go over what plugins (opens new window) are, how to install them, and how to configure them.
# Basics of Plugins
Plugins (opens new window) allow you to add global-level functionality to VuePress (opens new window). You can configure them by passing in options. It's also possible to write your own and publish them as npm (opens new window) packages.
To use a plugin you need to first install it by using either yarn
or npm
. We'll be using yarn
to install all of the plugins for the blog, but the commands for installing the plugins with npm
will also be provided. After installing a plugin, you can configure it by adding it to the config.js
file.
# VuePress Plugin - SVG Icons
Now that we know the basics, we're ready to install and configure vuepress-plugin-svg-icons (opens new window).
# Using the Tutorials Repo
If you're following along with the tutorials, then when you switch to the tutorial-8
branch you can run the following command to install the package instead of running the installation command:
This will ensure you have the same version used in the blog since the command uses the version specified in the yarn.lock
file during the installation.
# Using the Installation Command
To install the plugin in your own project you can run the following command:
Installing the Same Plugin Version
If you want to ensure you're installing the same version being used in the tutorials and blog, then run yarn upgrade @goy/vuepress-plugin-svg-icons@4.2.3
.
After installing the plugin, the package.json
file should look something like this:
Next we need to configure the plugin by editing the config.js
file:
To add the social media icons to the site we need to create an icons
directory in the docs
directory. Here's what the docs
directory should look like after adding the icons
directory:
.
├── docs
│ ├── .vuepress
│ ├── icons
│ └── README.md
The plugin recommends using iconfont (opens new window) to find Scalable Vector Graphics (SVGs) (opens new window) for your site. After creating an account, you can search for icons and download them. When downloading the icons you have the option to specify a color and size for the icon. For the blog we'll be using a color of #e6e6e6
and a size of 200
which is the default size.
You can download the icons from the tutorial-8
branch of the code-monkeys-blog-tutorials (opens new window) repository.
Using SVG Export
To easily download the icons used in the footer you can also install the browser extension SVG Export (opens new window). After installing the extension, all you need to do is click the extension icon which will extract all of the SVGs including their inline styles from the current page. A new tab will open containing all of the extracted SVGs which you can then download.
We'll be using icons for these social media sites as well as to an RSS feed for Code Monkeys in the footer:
- GitHub (opens new window)
- YouTube (opens new window)
- Gab (opens new window)
- Telegram (opens new window)
- Twitter (opens new window)
- RSS (opens new window)
- Keybase (opens new window)
After downloading the icons the icons
directory should look something like this:
├── icons
│ ├── Gab.svg
│ ├── GitHub.svg
│ ├── RSS.svg
│ ├── Keybase.svg
│ ├── Telegram.svg
│ ├── Twitter.svg
│ └── YouTube.svg
After adding the icons to icons
directory, the plugin will automically load the icons and will provide a global component named vp-icon
. To use the vp-icon
component you need to pass a name
attribute to it where the value is the name of the SVG file you want to use.
If you're interested in learning more about the plugin, then check out the vuepress-plugin-svg-icons (opens new window) documentation which contains more information about configuration options, component props, and command-line interface (CLI) commands.
We're now ready to create the custom footer component.
# Footer Component
The custom footer component will be added to the global-components
directory. Here's what the theme
directory will look like after adding the Footer.vue
file:
├── theme
│ ├── global-components
│ │ └── Footer.vue
Here we're creating a *.vue
file which is known as a Single-File Component (SFC) (opens new window). This is a special file format that allows you to encapsulate the HTML in a template
tag, the JavaScript in a script
tag, and the CSS in a style
tag for the component.
Here's what the Footer.vue
file looks like after adding these three main sections:
To start we'll add the HTML to the template
tag. The contents of the footer will be wrapped in a footer
tag containing two child tags a div
tag and a p
tag. The div
tag will contain seven child a
tags one for each of the social media icons. The p
tag will contain authorship information for the blog.
Each a
tag will have the following attributes: href
, target
, and rel
. The href
specifies the URL of the page the link goes to, the target
specifies where to open the link, and the rel
specifies the relationship between the current page and the link. Each a
tag will also have a child vp-icon
tag which will have the name
attribute discussed earlier.
Here's what the Footer.vue
file looks like after adding the HTML to the template
tag:
Each href
has a value of the related URL for the social media site. Each target
has a value of _blank
, and each rel
has a value of noopener noreferrer
.
As mentioned in the previous post these values for the target
and rel
attributes are used to prevent a vulnerability known as reverse tabnabbing (opens new window) which can happen when a user clicks on an external link. All major browsers have fixed this vulnerability, but you can still include these attributes in case a user is using a browser without this security update.
Each name
attribute has a value of the name of the related SVG file with the .svg
extension omitted.
Next we'll export the JavaScript in the script
tag and give the component a name. Here's what the Footer.vue
file looks like after updating the script
tag:
Finally, we'll style the component by adding the following CSS classes: "footer"
, "icons"
, and "made-by"
. We'll be adding the "footer"
class to the footer
tag, the "icons"
class to the div
tag, and the "made-by"
class to the p
tag. Then we'll add styling for each of the CSS classes in the style
tag. Here's what the Footer.vue
file looks like after styling:
The lang
attribute used in the style
tag is used to specify which pre-processor we want to use in the component. Here we're using Stylus (opens new window) which is the default pre-processor used by VuePress (opens new window). If you want to learn how to use a different pre-processor, then check out Using Pre-processors (opens new window).
A scoped
attribute is also used in the style
tag which means all of the styling only applies to the current component. Take a look at Scoped CSS (opens new window) to learn more about the scoped
attribute.
The "footer"
class is given display: flex
which defines a flex container for all of the direct children of the tag where the "footer"
class is used. In this case the "footer"
class is used on the footer
tag which means the div
tag and p
tag will be in a flex container. The "footer"
class is also given flex-direction: column
which means the flex items in the flex container, i.e., the div
tag and the p
tag will be stacked on top of each other in a column as opposed to the default row setting. The flex items are then given align-items: center
which will horizontally center the flex items since the flex direction is set to column. The "footer"
class is then given padding: 2.5rem 2.5rem 2rem
which will set a padding of 2.5rem
for the top, 2.5rem
for the left and right, and 2rem
for the bottom.
If you're unfamiliar with flexbox, then check out A Complete Guide to Flexbox (opens new window) and Basic Concepts of Flexbox (opens new window). Also, if you're unfamiliar with CSS units like rem
, then check out CSS Units (opens new window).
The "icons"
class is also given display: flex
which means each a
tag will be in a flex container since they're all direct children of the div
tag which has the "icons"
class. The "icons"
class is also given margin-bottom: 1.75rem
and font-size: 2rem
which increases the size of the icons. The CSS selector .icons > a:not(:last-child)
is then used which selects all a
tags where the parent tag has a class of "icons"
except for the last a
tag. Each a
tag that is selected is given margin-right: 4.6875rem
.
If you're unfamiliar with CSS selectors, then check out the CSS Selector Reference (opens new window).
Finally, the "made-by"
class is given margin: 0
which removes the margin from the p
tag.
If you didn't feel comfortable with the CSS discussed above, then here's a good resource to go through CSS Tutorial (opens new window).
As we continue to develop the blog, we'll add more styling to the footer to make it look presentable in different scenarios like on smaller screen sizes, when a sidebar is present, etc.
Here's what the Footer.vue
file should look like:
After adding the global-components
directory to the theme
directory, you may have noticed the site is rendering a blank page. This is because we created a directory in the theme
directory, so VuePress (opens new window) is now looking for a Layout.vue
file in the theme
directory.
We have the option of creating a layouts
directory and placing our own Layout.vue
file inside of it, but as previously mentioned we're going to use theme inheritance (opens new window) to inherit the default theme (opens new window).
# Theme Inheritance
Theme inheritance (opens new window) allows you to pass all of the capabilities from a parent theme to a child theme. In our case the parent theme is the default theme (opens new window), and we'll be creating the child theme as we develop the blog.
To create the child theme from the default theme (opens new window), we need to configure the extend (opens new window) option in the index.js
file.
First, we need to create the index.js
file in the theme
directory which should now look like this:
├── theme
│ ├── global-components
│ └── index.js
We can now configure the extend (opens new window) option:
The inherited default theme (opens new window) should now be rendering alongside the child theme instead of the blank page.
Missing Layout.vue File
You may have noticed that the Layout.vue
file is listed as a mandatory file, but we didn't need to create one in the theme
directory. This is because you don't need to explicitly create the Layout.vue
file when you're inheriting it from the default theme (opens new window).
The child theme is also able to override files in the parent theme by creating a file with the same name in the same location. We'll go over how to override parent theme files in more detail in future tutorials. You can also override some parent theme files by just using the same name as the parent theme file in the child theme. For example, we're going to override the GlobalLayout.vue
file by using the same name for the file in the child theme and the code provided by the documentation.
Now let's create the layouts
directory in the theme
directory. This is where we'll be adding the GlobalLayout.vue
file which again allows us to add our custom footer component to the global layout of the site.
# Using the Global Layout
Here's what the theme
directory will look like after creating the GlobalLayout.vue
file in the layouts
directory:
├── theme
│ ├── global-components
│ ├── layouts
│ │ └── GlobalLayout.vue
│ └── index.js
The GlobalLayout.vue
file is responsible for handling the global layout of the site. Here's the path to the default GlobalLayout.vue
file provided by VuePress (opens new window) node_modules/@vuepress/core/lib/client/components
. You can also view the code for the default global layout here (opens new window).
The default global layout gives you the ability to render different layouts depending on if $page.path
is defined, i.e., the URL of the page is valid and if $page.frontmatter.layout
is defined. If only the $page.path
is defined, then the default layout will be used which is Layout
. If $page.frontmatter.layout
is defined, then the specific layout for that page will be used. If $page.path
is not defined, then the NotFound
layout is used which is the layout for the 404 page provided by VuePress (opens new window). In most scenarios you don't need to edit the default global layout, but if you want to add a global header or a global footer to your site, then this is a good place to add it.
To override and edit the GlobalLayout.vue
file we're going to use the code provided by the globalLayout (opens new window) section from the documentation. The code provided by the documentation basically uses a computed property (opens new window) to determine the layout for a page based on if the URL of the page is defined and if the page uses a specific layout provided by the frontmatter. This is similar to the logic previously described when discussing the default global layout.
Here's the code from the documentation:
We're going to remove the example header
tag and replace the example footer
tag with our previously created footer component.
Here's what the GlobalLayout.vue
file should look like after the changes:
Importing the Footer Component
Notice that we don't need to explicitly import the footer component since we made it a global component by adding it to the global-components
directory in the theme
directory.
# Next Steps
In the next tutorial we'll discuss how to override the index.styl
and palette.styl
files to add our own global styling to the site. The global styling will include background color, accent color, text color, etc.