How I Built This Website

I wanted to create a personal website that reflected who I am and some of the things I believe. This means several things, but on a base level I settle for two main concepts that were the drivers for this: minimalism and simplicity.

  • minimalist: I have several identical plain black T-shirts that I use for almost everything. It makes sense for my website to reflect this part of my personality.
  • simplicity: I'm a Linux user, more specifically an Arch Linux user. Arch is known for their view on simplicity without unnecessary additions or modifications. On the web, there is nothing simpler than HTML, but I also want to have decent visuals so there's no escaping CSS too. Trendy choices like Next.js and Gatsby are of the question because they have many layers and add unnecessary boilerplate.

The Project I Enjoyed The Most On College

Hopefully, this backstory will give more insight into what I like. I graduated in Electrical Engineering and the project I'm most proud of and had the most fun was building a bookshelf! We had the dimensions and mass of a specific book and we had to design a shelf to hold the book. It also had other requirements like the material to be used, the angle of inclination, and the height from the surface to the book base.

Drawing of the book shelf because I wanted to use my Wacom tablet

The catch is that the shelf had to break when the mass of the book exceeded 10%. All groups had to bring their shelf to the class and the professor would test one by one. First asking where it would break, then he'd place the book on the shelf. If the shelf didn't break he'd add 10% more mass using A4 sheets. Groups in which the shelf broke with 10% extra weight gained an A. Shelves that needed more than 10% to break out get a B, C, or D depending on the weight needed. This was my favorite project because we had specifications and had to design something for those exact specifications, no less, no more.

This website is no different. I wanted a minimal version that supplies my needs, no less, no more.

Design

I love the subject of user experience, but designing is not really my thing. So I went with the most simple stuff I could think of. A section on the left serves as a "header" and the main content on the right. On mobile, the section on the left is displayed at the top. If instead of the left section I had decided to do a conventional header at the top, the mobile scenario would be more problematic. I would need to create a hamburger menu, have a menu offscreen that moves when the hamburger is clicked, hide the scrollbar when the menu is being displayed, etc. At a job, I would do this in no time, but on my personal site? Too much trouble.

For colors, I went with white, black, tones in-between, and blue because it's one of my favorite colors. Typography I copied from other places and settled on Merriweather for the text and Raleway for everything else. The website has no other icons beside the socials for GitHub and LinkedIn. For these icons, I used the Font Awesome but got the SVG version of them which was just embedded in the HTML itself.

Stack

I wanted my site to be statically generated, where pages are generated at build time. Reasons:

  • easy to deploy in several places because is just HTML and CSS
  • I don't have to worry about security, updating things, etc
  • not a problem I'm going to have, but it scales easily

I knew these static generations tools existed: Next.js, Gatsby, Jekyll, and Hugo. I'm familiar with Next.js and Gatsby, but they are overkill and use React, which is bloatware that I don't need. (UPDATE: I discovered that NextJS allows to disable JavaScript). Jekyll is written in Ruby, while Hugo is written on Go, I'm not familiar with either of these languages. This is not a problem, but I feel working with a tool written in a language you're familiar with is smarter. My bet was that a similar framework to Jekyll and Hugo existed in JavaScript which would allow me to use the whole JS ecosystem and I'd need to learn a new language.

Searching for such a tool I found Eleventy (11ty) and that's what I decided to use. It allows any directory structure you want, support several template engines, and it doesn't add a single bit of boilerplate to the output.

The file structure (generated with tree -I "_site|node_modules|.git" -a):

├── 404.liquid
├── about.liquid
├── assets
│ ├── apple-touch-icon.png
│ ├── favicon.ico
│ ├── icon-192.png
│ ├── icon-512.png
│ └── icon.svg
├── blog
│ ├── blog.11tydata.json
│ ├── how-i-build-this-website
│ │ ├── index.md
│ │ └── shelf.webp
│ ├── introduction-to-git
│ │ └── index.md
│ ├── my-terminal
│ │ └── index.md
│ └── solving-package-lock-json-conflicts
│ └── index.md
├── _data
│ └── metadata.json
├── .eleventy.js
├── favicon.ico
├── fonts
│ ├── merriweather-v27-latin-regular.woff
│ ├── merriweather-v27-latin-regular.woff2
│ ├── raleway-v22-latin-700.woff
│ ├── raleway-v22-latin-700.woff2
│ ├── raleway-v22-latin-regular.woff
│ └── raleway-v22-latin-regular.woff2
├── .gitignore
├── _includes
│ ├── blog.liquid
│ ├── default.liquid
│ ├── main.css
│ ├── normalize.css
│ └── post-metadata.liquid
├── index.liquid
├── manifest.webmanifest
├── package.json
├── package-lock.json
├── styles
│ └── prism.css
└── tag.liquid

Let's take a look at some of these files.

index.liquid

---
title: Blog
layout: default.liquid
---
<h1 class="page-title">{{title}}</h1>
<ul class="post-list">
{%- for blog in collections.blog reversed -%}
{% if blog.data.published == true %}
<li class="post-list__item">
<a href="{{blog.url}}" class="post-title">{{blog.data.title}}</a>
{% assign tags = blog.data.tags %}
{% assign date = blog.data.date %}
{% include post-metadata.liquid %}
<p class="post-list__description">{{blog.data.description}}</p>
</li>
{% endif %}
{% endfor %}
</ul>

I specify some metadata at the top of the file. The title is a variable that will be available when generating the final HTML. I use it on the h1 tag and also on <title> and title meta tags. The layout specifies that this content will be the child of default.liquid.

The actual content here is just looping over the blog collection and displaying them as a list. I also include a component called post-metadata.liquid because this is the same component that is displayed when viewing a blog post. I actually missed React in this part, I'm too used to using components and passing props around

default.liquid

Simplified version:

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Personal blog by Marcelo Fernandes">
<meta name="author" content="Marcelo Fernandes">
<title>{{title}} | {{metadata.name}}</title>
<!-- Open Graph -->
<meta property="og:title" content="{{title}}">
<meta property="og:type" content="website">
<meta property="og:url" content="{{metadata.url}}{{page.url}}">
<meta property="og:description" content="{{description}}">

<!-- Twitter tags -->
<meta name="twitter:card" content="summary">
<meta name="twitter:url" content="{{metadata.url}}{{page.url}}">
<meta name="twitter:title" content="{{title}}">
<meta name="twitter:description" content="{{description}}">

<!-- Favicon -->
<link rel="icon" href="/assets/favicon.ico" sizes="any"><!-- 32×32 -->
<link rel="icon" href="/assets/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png"><!-- 180×180 -->
<link rel="manifest" href="/manifest.webmanifest">

{% capture css %}
{% include normalize.css %}
{% include main.css %}
{% endcapture %}
<style>
{{ css | cssmin | safe }}
</style>

<noscript>
<link rel="stylesheet" href="/styles/prism.css">
</noscript>
</head>
<body>
<div class="container">
<section class="sidebar">
...
</section>
<main>
<div class="divisor"></div>
{{content}}
</main>
</div>
</body>
</html>
<script>
const prismStyle = document.createElement("link")
prismStyle.rel = "stylesheet"
prismStyle.type = "text/css"
prismStyle.href = "/styles/prism.css"
document.head.appendChild(prismStyle)
</script>

The content for the meta tags is dependent on each page. There is also the content variable which is where our previous index.liquid is placed.

blog.liquid

---
layout: default.liquid
---
<article class="post-article">
<h1 class="post-title post-page-title">{{title}}</h1>
{% include post-metadata.liquid %}
<p>{{ description }}</p>
{{ content | safe }}
</article>

Here I chained a layout on another layout. This is the layout foundation used on all blog posts.

blog.11tydata.json

{
"layout": "blog.liquid"
}

This file is located on the blog folder, it sets all pages below this folder to use the blog.liquid layout.

index.md

This is an example of what a blog post looks like.

---
title: How I Built This Website
description: "I wanted to create a personal website that reflected who I am and some of the things I believe. This means several things, but on a base level I settle for two main concepts that were the drivers for this: <strong>minimalism</strong> and <strong>simplicity</strong>."
date: 2021-11-26
tags:
- blog
- front-end
- website
published: true

---


## My Actual Content Goes Here

Neat!

CSS

For managing the CSS mess and components I loosely followed the BEM methodology. I also used Normalize.css to not have to deal with browser problems.

Syntax Highlight

11ty got you covered on their documentation. I'm using the Dracula theme because it's what I use on my code editors.

Optimizations

Well, as I said in the beginning I like to be efficient. Some of the things I did:

  • minify: all HTML, CSS, and JS are minified to reduce the size.
  • loading CSS: normalize and my CSS file are small, so I decided to inline them. The Prism theme is loaded from an external file which allows it to be cached. Placing the CSS in the <head> blocks the rendering of the page until the file is downloaded. However, I decided to due that because there were some layout shifts that I didn't like.
  • graphics: the social icons are SVG because it's scalable and small in size. The image format for images on blog posts is webp. I'm also compressing them with tinypng.
  • fonts: initially I was using Google Fonts, but this requires connecting to the Google servers to download the font, and placing them in the <head> it also blocks the rendering. An alternative to that is hosting the fonts yourself, which takes advantage of HTTP/2 multiplexing. To get the fonts there is an awesome tool for that: google-webfonts-helper.

For testing this optimizations I used WebPageTest and LightHouse.

Deployment

Netlify on their free plan. Netlify is awesome and I highly recommend to check it out, their free plan is very generous.

Ending Notes

I'm quite happy with Eleventy. I had no problem with it and was able everything I wanted by reading the docs and searching on the internet. My biggest con is that their documentation is not that well organized.