Making a portfolio using Webpack, 11ty, and Halfmoon
Tags: tutorial, webpack, 11ty, halfmoon
Published: 2020-10-22 22:58:26

## Table Of Contents
* [🛍 Resource Shopping/Setting up](#resource_shopping)
* [📦 Webpack config](#webpack_config)
* [⚙ Setting up 11ty](#11ty_setup)
* [👨‍💻 Coding our base templates](#coding_base_templates)
* [📄 Making the portfolio](#making_the_portfolio)
* [🏭 Making it "production" ready](#making_it_production_ready)
* [📔 Notes & Credits](#notes_and_credits)




## <a name="resource_shopping" href="#resource_shopping">🛍 Resource Shopping/Setting up</a>

Ok, what do I mean by resource shopping? We go around the internet looking ("shopping") for resources (e.g. Bootstrap, jQuery plugins, etc) that we will use in our project. For this mini-project, I've chosen:
- [Halfmoon](https://gethalfmoon.com) for our design
- [11ty](https://11ty.dev/) to compile our base templates
- [Webpack](https://webpack.js.org/) to compile our assets (Halfmoon, Sticky Sidebar, etc)
- style-loader & css-loader for Webpack so we can compile CSS

Lets install their NPM packages for Webpack so we can bundle them up:
```sh
npm i @11ty/eleventy -g -s
npm i webpack webpack-cli -s -d
npm i halfmoon style-loader css-loader
```
**Note:** Webpack 5.0.0-beta.1 and higher must use a node version higher than 10.13.0 (LTS). So, since I'm using Glitch to create this, I'm going to change in the package.json:
```json
"engines": { "node": "10.x" }
```
to 
```json
"engines": { "node": "12.x" }
```
and add a .nvmrc (for netlify) with just `12.9.0`. If you are not using Netlify or Glitch, then just make sure you have a higher version installed.

Ok, we have our node versions setup and packages installed, lets start with webpack.

## <a href="#webpack_config" name="webpack_config">📦 Webpack config</a>

Projects that use a bunch of files probably should use something called Webpack. Webpack basically allows you to bundle your assets into 1 (or more) files for your browser. Pretty cool right?

Let's make a file called `webpack.config.js` with the following:
```jsx
const path = require("path");

module.exports = {
  entry: "./src/index.js",
  mode: 'development',
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist")
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  }
};
```
This basically says "Bundle the file src/index.js into .dist/bundle.js"
(oh and by the way, make the file `src/index.js` and the folder `dist` because we will need it in a moment)

In our src/index.js file, let's require our files/packages that we went over in <a name="resource_shopping" href="#resource_shopping">Resource Shopping/Setting up</a> with this code:
```jsx
require("halfmoon/css/halfmoon.min.css"); // require halfmoon.css
window.halfmoon = require("halfmoon"); // require halfmoon.js
console.log("Webpack loaded!"); // Show in the console that the webpack bundle is loaded.
```
Now we can bundle them together! Just run `webpack` and it should bundle your stuff together! Your bundle should be in `dist/bundle.js` if you made it right.

That's all you need to do to bundle your modules together.

##  <a href="#11ty_setup" name="11ty_setup">⚙ Setting up 11ty</a>
This will be a shorter section because 11ty does not need any config to run.

Make a folder called `_includes`. Then in that folder, make a file called `base.njk` with the following:
```html
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="HandheldFriendly" content="true">
</head>

<body>
{{ content | safe }}
<footer>

</footer>
</body>
```
That's right. `.njk` files (nunjuck files) use HTML! But, whats this `{{ content | safe }}` thing? Well, since `_includes/base.njk` is a layout, we can tell our pages (like index.njk) to use `_includes/base.njk` as a layout.

Now, we can make our `index.njk` file (which will be located at `/` when we compile it.) Lets just insert:
```md
---
layout: base.njk
---
<h1>Hello!</h1>
```
in that file. Before we run it, let's do some automation to make it easier for us to code our portfolio. In our `package.json` file add:
```json
"scripts": {"start": "eleventy --serve --port=3000"}
```
to your file. Now you can visit your demo site at locathost:3000 (or just your regular domain if you are using Glitch). It should look something like this:
![hello_world](https://dev-to-uploads.s3.amazonaws.com/i/r01ncvnzpmncfy4j2xz4.png)

Yay! We have used successfully used 11ty layouts! One last thing: We need to make sure 11ty adds our bundle. So make a new file called `.eleventy.js` and add the following:
```jsx
module.exports = function(eleventyConfig) {
  eleventyConfig.addPassthroughCopy("img");
  eleventyConfig.addPassthroughCopy("dist");
};
```
This tells 11ty to allow the img folder and the dist folder.

## <a name="code_portfolio" href="#coding_base_templates">👨‍💻 Coding our base templates</a>

Ok, lets get into the design part! Go back to our base layout and include our bundle:
```html
<script src="/dist/bundle.js"></script>
```
HalfmoonCSS, HalfmoonJS, and Sticky Sidebar should now be included in your webpage!

Now, why don't we add a navbar? Like many sites, this allows your users to easily navigate your site.

Add the following code to your `_includes/base.njk` file inside the "body" tag:

```html
<!DOCTYPE html>
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="HandheldFriendly" content="true">
</head>
<body data-set-preferred-mode-onload="true">
<!-- Navbar: https://www.gethalfmoon.com/docs/navbar/ -->
<header>
<nav class="navbar" style="background-image:url('https://cdn.glitch.com/d4417ab9-7b55-405a-bfbf-6194880aa9f7%2Fmarek-piwnicki-bNnYAs5pXbw-unsplash.jpg?v=1603332064144');">
  <!-- Navbar brand -->
  <a href="/" class="navbar-brand">
    <img src="https://cdn.glitch.com/d4417ab9-7b55-405a-bfbf-6194880aa9f7%2Fninja-cat_1f431-200d-1f464.png?v=1603305812684" alt="...">
  </a>
  <!-- Navbar nav -->
  <ul class="navbar-nav ml-auto"> 
    <li class="nav-item active">
      <a href="/" class="nav-link">Home</a>
    </li>
    <button class="btn btn-primary" type="button" onclick="halfmoon.toggleDarkMode();">🌙</button>
  </ul>
</nav>
</header>
{{ content | safe }}
<footer>
<script src="/dist/bundle.js"></script>
</footer>
</body>
```

![first demo](https://dev-to-uploads.s3.amazonaws.com/i/c5tigjrrmyef5u66rfcw.png)

Hey! Did you see that 🌙 button? That's the darkmode toggle, which allows you to toggle on and off dark mode! It looks something like this:

![dark mode](https://dev-to-uploads.s3.amazonaws.com/i/q53tygq4ta5dpck0klv8.png)

Pretty cool right? You can trigger it yourself using:
```jsx
halfmoon.toggleDarkMode();
```
 
## <a href="#making_the_portfolio" name="making_the_portfolio">📄Making the "portfolio" part</a>

Let's get rid of the "Hello" header:
```diff
---
layout: base.njk
---
- <h1>Hello!</h1>
```

We can now make "about me" cards and show off our knowledge of certain skills.

For this part, we will be using:
- [Progress bars](https://www.gethalfmoon.com/docs/progress/)
- [Containers](https://www.gethalfmoon.com/docs/containers/)
- [Cards](https://www.gethalfmoon.com/docs/content-and-cards/).

Make a new file called "about.njk":

```md
---
eleventyExcludeFromCollections: true
---
<div class="container-fluid" style="padding-left: 5px;">
    <div class="card">
        <h4>Who am I?</h4>
        <img src="https://cdn.glitch.com/d4417ab9-7b55-405a-bfbf-6194880aa9f7%2Fwilliam-recinos-qtYhAQnIwSE-unsplash.jpg" width="225" height="150" />
        <p>My name is John Doe. I am a freelance web devoloper with many years of expierence. I also play the violin the my spare time.</p>
        <h2 class="card-title">Skills</h2>
        <p>HTML</p>
        <div class="progress">
            <div class="progress-bar" style="width: 78%;" role="progressbar" aria-valuenow="78" aria-valuemin="0" aria-valuemax="100"></div>
        </div>
        <p>CSS</p>
        <div class="progress">
            <div class="progress-bar" style="width: 64%;" role="progressbar" aria-valuenow="54" aria-valuemin="0" aria-valuemax="100"></div>
        </div>
        <p>Javascript</p>
        <div class="progress">
            <div class="progress-bar" style="width: 54%;" role="progressbar" aria-valuenow="54" aria-valuemin="0" aria-valuemax="100"></div>
        </div>
        <p>Ruby</p>
        <div class="progress">
            <div class="progress-bar" style="width: 32%;" role="progressbar" aria-valuenow="32" aria-valuemin="0" aria-valuemax="100"></div>
        </div>
    </div>
</div>

```

And add this to your main index.njk file:

```
{% include "about.njk" %}
```
this should include the about.njk file into the index.njk file. 

You should see something like this:
![about cards](https://dev-to-uploads.s3.amazonaws.com/i/8c96t777ycwtwem3ukcl.png)
  
You can add new skill bars by adding this:

```html
<p>Skill Name</p> 
<div class="progress"> 
<div class="progress-bar" style="width: value%" role="progressbar" aria-valuenow="value" aria-valuemin="0" aria-valuemax="100"></div> 
</div>
```

Alright looking good. We also want to show off our projects as any good web developer would do. 

Make a new file called "projects.njk" and add the following:

```md
---
eleventyExcludeFromCollections: true
---
<div class="container-fluid" style="padding-left: 5px;">
    <div class="card">
        <h4>Projects:</h4>
        <ul>
            <li><a href="#"><b>Project one:</b></a> Project description</li>
            <li><a href="#"><b>Project two:</b></a> Project description</li>
            <li><a href="#"><b>Project three:</b></a> Project description</li>
        </ul>
    </div>
</div>
```

Now we can modify our index.njk file to look like this:

```diff
{% include "about.njk" %}
+
+ {% include "projects.njk" %}
```

Great! Now we the about section and our project showcase. We now have a semi-portfolio using Webpack. 

## <a href="#making_it_production_ready" name="#making_it_production_ready">🏭 Making it "production" ready</a>

Ok, let's make it production ready. Bascially, Polishing every thing up, doing some SEO, etc.

#### Changing webpack settings

The webpack bundle we made is huge! So let's tell webpack to remove extra stuff and compress the file. Go into the webpack.config.js file and modify it slightly:

```diff
const path = require("path");
+ const { CleanWebpackPlugin } = require("clean-webpack-plugin");
module.exports = {
  entry: "./src/index.js",
-  mode: "development",
+  mode: "production",
  output: {
    filename: "bundle.js",
    path: path.resolve(__dirname, "dist")
  },
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ["style-loader", "css-loader"]
      }
    ]
  },
+  plugins: [new CleanWebpackPlugin()]
};
```

Oh and don't forget to install clean-webpack-plugin, which deletes your old bundle so no old data is stored.

```sh
npm i clean-webpack-plugin
```

You may now run `webpack` and you should now have a production ready bundle!

#### Improving SEO: Sitemap

Lets make a sitemap. This should allow us to submit the portfolio to Google easier ([source](https://github.com/11ty/eleventy-base-blog/blob/master/sitemap.xml.njk)):

```html
---
permalink: /sitemap.xml
eleventyExcludeFromCollections: true
---
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
{%- for page in collections.all %}
  {% set absoluteUrl %}{{ page.url | url  }}{% endset %}
  <url>
    <loc>https://{{ metadata.domain }}{{ absoluteUrl }}</loc>
  </url>
{%- endfor %}
</urlset>
```

#### Improving SEO: Metatags

Metatags allow for website previews. They give your users a preview of your website without having to see it. Sort of like a "try before you buy" thing. You can easily generate some easily with [metatags.io](https://metatags.io/). They look like this:

```html
<!-- Primary Meta Tags -->
<title>Demo portfolio: The portfolio of John doe</title>
<meta name="title" content="Demo portfolio: The portfolio of John doe">
<meta name="description" content="My name is John Doe. I have a bunch of experience with HTML, CSS, JavaScript and Ruby.">

<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://demo-portfolio-11ty.glitch.me/">
<meta property="og:title" content="Demo portfolio: The portfolio of John doe">
<meta property="og:description" content="My name is John Doe. I have a bunch of experience with HTML, CSS, JavaScript and Ruby.">
<meta property="og:image" content="[IMAGE]">

<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://demo-portfolio-11ty.glitch.me/">
<meta property="twitter:title" content="Demo portfolio: The portfolio of John doe">
<meta property="twitter:description" content="My name is John Doe. I have a bunch of experience with HTML, CSS, JavaScript and Ruby.">
<meta property="twitter:image" content="[IMAGE]">
```

#### Compressing your HTML

This is pretty easy to do. First install html-minifier with NPM:
```sh
npm i html-minifier
```

Add the following snippet to .eleventy.js:

```jsx
const htmlmin = require("html-minifier");
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
    if (outputPath.endsWith(".html") || outputPath.endsWith(".css") || outputPath.endsWith(".js")) {
        let minified = htmlmin.minify(content, {
            useShortDoctype: true,
            removeComments: true,
            collapseWhitespace: true,
            minifyJS: true,
            minifyCSS: true,
        });
        return minified;
    }

    return content;
});
```
This tells 11ty to use html-minifier to compress HTML, JavaScript, and CSS files.
![compressed html](https://i.imgur.com/yHF7gBJ.png)

Looking good!

## <a href="#notes_and_credits" name="notes_and_credits">📔 Notes & Credits</a>

In this tutorial, we used Webpack to bundle assets (a simple bundle at that), 11ty to make our portfolio, and Halfmoon for the main design.

Thanks to @khalby786 for making [brockly](https://brockly.glitch.me/), which made the browser-like previews.

Some snippets of code (such as the sitemap and html mimifier came from [11ty/eleventy-base-blog](https://github.com/11ty/eleventy-base-blog/blob/master/sitemap.xml.njk) and the [11ty docs](https://www.11ty.dev/docs/).

You can get a ready made version on Glitch by clicking [here](https://glitch.com/edit/#!/remix/demo-portfolio-11ty) (make an account and edit the _data/metadata.json file)

You can see the demo [here](https://demo-portfolio-11ty.glitch.me/)