Creating a Fast ⚡ and Beautiful 🌼 Portfolio Website using HTML, CSS 🎨, Eleventy and Netlify
Tags: webdev, webperf, eleventy, netlify
Published: 2021-02-23 14:23:05

Eleven years ago, in 2010, my personal website was minimal by every means. Relaunching in 2021 using Eleventy and modern CSS was fun and brought with it some unexpected insights.

### How It Started, How It's Going ...

In 2010, I registered my personal domain and put up a minimal HTML file writing some text about me doing web development, web design and charitable work. I already forgot, but [web archive remembers](https://web.archive.org/web/20120619092112/http://www.ingo-steinke.de:80/).

![Screenshots of Ingo Steinke's website in 2012 and in 2021](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bw07oyd48w9e1gvzw52w.jpg)

In 2021, I wanted to keep things light-weight with a focus on page speed, usability and accessibility. All information should fit on one page, so it can be printed out as a resume with a CV, project list, and skill matrix.

## Choosing a "Stack"

![Desk with a laptop, a lamp, and a stack of actual jars filled with jam](https://dev-to-uploads.s3.amazonaws.com/i/sxd2ja76ln2xciqluih4.jpg)

Lamp stack or jam stack? 💡🍯

[A static site generator](https://www.11ty.dev) fitted my needs, and I liked the effortless deployment on [Netlify](https://www.netlify.com), once I pushed code to [GitHub](https://github.com/openmindculture/ingo-steinke.de/). 

### Eleventy + Netlify === JAMStack ?

In my opinion, my setup is not necessarily a [JAMStack](https://jamstack.org) just because I used Eleventy and Netlify. My portfolio does not use any API. Front-end JavaScript is optional, only there to improve the mobile menu experience. My development tools, like StyleLint, PostCSS, Babel, and Cypress depend on node.js, so there is more JavaScript involved at least under the hood.

Despite its complexity at build time, the result is a static website based on a single HTML page, enhanced with some CSS and very little JavaScript.

## Core Web Vitals and Progressive Enhancement

[Google's Core Web Vitals](https://web.dev/vitals/#core-web-vitals) emphasize usability and web performance for search engine optimization. I knew that progressive enhancement can benefit both usability and performance, but now I know how much it still matters in 2021.

As my project started from scratch, it started with top performance scores on PageSpeed Insights. While adding content and decoration, I made sure to re-test regularly. My goal: maintain 90% - 100% PageSpeed scores, and "A" or "B" ratings using WebPageTest or GTMetrix:

![Screenshot of GTMetrix performance report for www.ingo-steinke.de on February 15, 2021](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5qc66rez16cyyrsgha04.png)

Google's emphasis on mobile and usability optimization made me go for a progressive enhancement strategy starting with a plain semantic HTML template. 

### Building Upon the Essentials

Using [semantic HTML 5 markup](https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML), I put my existing page content into a new template and set up a basic build and deployment toolchain. When adding Style Sheets, I made the occasional check without CSS or rather by looking at its print preview.

### Print First, Images Second?

Printing your website is an effective way to inspect your website without interactivity. If you already use [native lazy loading](https://addyosmani.com/blog/lazy-loading/) in Chrome, you can even print a page [without images below the fold](https://github.com/whatwg/html/issues/2532) (which, in this case, is a bug, not a feature). In the end, my designer advised me to replace my project screenshots with decorative vector icons, due to optical reasons, so all images below the fold are now optional anyway.

While print preview shows a more realistic output, emulating CSS media type print in the browser's developer tools will also allow you to inspect and edit the effective style directives. Media type emulation is a little bit hidden in the console drawer's Rendering tab, so I prefer the quicker "Run Command" (`Ctrl+Shift+P`) and search for "print".

![Chrome browser developer tools suggesting features when searching for media type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/senzznhtyu34xe5lzixr.png)

### Critical Style Sheets

I split my styles into several files. Critical styles, that are necessary for layout, colours and typography, will be included inline. Most other styles will be loaded depending on the media type, screen or print, and the optional animation is deferred until page load.

Splitting files, defining media types, and respecting user preferences like [prefers-reduced-motion](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-reduced-motion) will spare the browser loading and parsing style definitions that might not be needed at all.

The included file for colour definitions will only be processed by PostCSS, so I can use custom properties (sometimes also known as CSS variables) in my code while still exporting backwards-compatible code without dynamic properties and unnecessary `calc()` calculations.

![Screenshot of a stylesheet file containing colour properties shown in a colourful preview by a Visual Studio Code plugin](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/izgoyysmtlbegoef7ulz.png)

### CSS Can Do That?

Although I knew I did miss some web development progress in the past eleven years, I thought I knew CSS quite well, which is why I wanted to make heavy use of it in the first place. I found out, that you can really build a beautiful, responsive and, to some intent, interactive website with just HTML and CSS and no JavaScript at all.

#### CSS Grid with Flexbox Fallback

This CSS grid definition includes a flexbox fallback. By design, grid layout overrides flex layout, so modern browsers will ignore any flex behaviour of the grid and its child elements. This works good enough even in Internet Explorer 10 and 11, so we do not need to specify any `-ms-grid-` directives, except for a one-column mobile view.

#### Code

```css
.grid-container {
  display: flex;
  display: grid;
  clear: both;
  grid-template-columns: repeat(auto-fit, minmax(440px, 1fr));
  grid-column-gap: 3em;
  grid-row-gap: 2em;
  flex-wrap: wrap;
  justify-content: space-between;
}

.grid-container > * {
  flex-basis: 49%;
  margin-bottom: 2em;

  @supports (display: grid) {
    margin-bottom: 0;
  }
}
```

As a fallback for browsers that neither support `grid` nor `flex`, you can define `float`, but then you might have to `clear` the floats, like I had to do in the above example).

Once an item becomes a flex or grid child, its float will be ingored by modern browsers.


### No Intrinsic Web Design

The idea of intrinsic web design, defining responsive layouts by context rather than viewport width, is impossible to achieve without [container queries](https://css-tricks.com/minimal-takes-on-faking-container-queries/). At least I managed to use all but one breakpoint thus avoiding all the fine-tuning pixel-pushing that used to go along with it.

Reset gutters on small viewports:

```css
@media only screen and (max-width: 440px) {
  .grid-container {
    grid-template-columns: 1fr;
    -ms-grid-columns: 1fr;
    grid-column-gap: 0;
    grid-row-gap: 1em;
  }
}
```

### Specificity Issues

I was also impressed by StyleLint showing me warnings about unintended specificity issues. 

![Screenshot of a tooltip with style lint output, expecting selector .vcard a:hover to come before selector .social-links a:not(a:hover) to avoid descending specificity](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6dr4x78uxadwlv6dl51s.png)

I have to admit that even those few lines of code can soon grow to a complexity that is hard to handle, especially with arbitrary class names that do not follow any naming concept like BEM or CUBE CSS.

### Step by Step

Small stories and [trunk-based development](https://trunkbaseddevelopment.com) helped me not to sink into chaos. Every major component, like the grid layouts for projects and skills, the sticky header, and the mobile menu, has a small preview file, that can serve as a minimal verifiable example to copy to [Codepen](https://codepen.io/ingosteinke) or [StackOverflow](https://stackoverflow.com/users/5069530/ingo-steinke) when external help is needed.

### Keeping it Simple

I have seen so many overly complex code that I stopped blaming myself when I fail to understand something easily. [Simplicity and robustness have made HTML and CSS outlive many other technologies](https://hiddedevries.nl/en/blog/2020-11-25-why-its-good-for-users-that-html-css-and-js-are-separate-languages).

Building a small project on your own lacks the challenge of constant code review. But you do not have to rely on your being disciplined to write maintainable code. We have got tools!

## Inspect and Audit

Use automated tools like [Lighthouse](https://developers.google.com/web/tools/lighthouse/), [axe](https://www.deque.com/axe/), and your browser's developer tools to find issues and errors early to ensure your site does not completely fail when it comes to speed, usability and accessibility.

Chrome and Firefox offer detailed inspections of a website's grid and flexbox layouts, web fonts and animations. Some are hard to find, unless you search for them in "Run Command" (Ctrl+Shift+P), like layers, animations, and media type emulation.

### Layers and Animations in Developer Tools

![Screenshot of browser developer tools showing details of layers and animations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/io9a6yp1pcmlch05z5lo.png)

Page speed measurement can vary erratically, so do not forget to click "re-analyze" on [Google's PageSpeed Insights](https://developers.google.com/speed/pagespeed/insights/) results. At [webpagetest.org](https://www.webpagetest.org) you can specify the number of test runs and many other browser settings in advance. They also offer a wide range of browsers and actual devices in many places around the globe. If you prefer a more polished design, [GTmetrix](https://gtmetrix.com) might be for you. Another alternative, [Yellow Lab Tools](https://yellowlab.tools), does not highlight Core Web Vitals (yet), but it does show some unique metrics like DOM complexity, JavaScript errors, or CSS redundancy.

### Web Fonts causing Layout Shift

Ironically, some of the advice you will find in Google's Lighthouse discourages code created by other Google tools! 

[Using font-display swap](https://css-tricks.com/really-dislike-fout-font-display-optional-might-jam/) to prevent a flash of invisible text (FOIT) will cause a flash of unstyled text (FOUT) instead which in turn will add to the cumulative layout shift. Relying on web-safe fonts only would mean missing out on a lot of appealing typographic options.

I did not find a perfect solution yet, but using a full-height header as a cover of my website helped me reduce layout shift to nearly zero.

![Nearly no layout shift, according to Addy Osmani's Web Vitals extension](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/myqda8qmtxc39zw77gqe.png)

## Content: the Hard Parts

Writing about myself was harder than any technological challenge. But when I want people to work with me, I should give them a reason to trust me. Also, I wanted to create some more content to [feed the search engines](https://moz.com/blog/google-e-a-t).

### From Full-Stack to Creative Web Developer

Am I a full-stack developer? I have worked with front-end technology like HTML, CSS and JavaScript, as well as back-end technology like PHP, Symfony, SQL, and I even set up local development stacks and deployments to Jenkins, Rancher, or Netlify.

Once a good friend told me "if you call yourself a full-stack developer, you will only get back-end jobs", I changed my claim to be a front-end developer. As a result, job offers changed to React and Angular developer.

So I started to examine what exactly made me feel grumpy about those React projects of the past and what kind of projects would possibly make me happy and motivated in the future, like building beautiful websites on a minimalistic technology stack.

While my portfolio's web design and technology stack should speak for itself, I still had to find the right words to express my stance and came up with "creative web developer"

## Style Checking

[Stilcheck](https://stilcheck.lianehein.de), an online tool to improve German language style, shamed my first drafts by putting every imaginable criticism in nearly each of my sentences. One of the few things they do not check is spelling, so I ended up publishing a nice text full of misspellings before I noticed.

![Screenshot of a German text style checking tool](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmxl519he1ze2b37uhu2.png)

## Internationalization

To my surprise, adding a second language to my portfolio proved a major task in many aspects, besides providing the actual translations.

### Another Domain for Each Language

Domains or Directories? At least, in this case, Google offers some clear advice as to the world's most popular search engine: alternative content in another language is best served on another domain, using semantic markup to indicate their relationship, like this:

#### Code

```html
<html lang="en">
<head>
  <meta name="language" content="en">
  <link rel="alternate" hreflang="de" href="https://www.ingo-steinke.de/">
  <link rel="canonical" href="https://www.ingo-steinke.com/">
```

Hard to believe, but I had a German website for eleven years without ever thinking about registering the matching `.com` domains. Neither did the other Germans that go by the same name, so I registered my additional domains and wondered which would be the best choice of a canonical URL?
`www.ingo-steinke.com`?
`www.ingosteinke.com`?
`ingosteinke.com`?

### "Worldwide Web"

Long gone the day when everyone expected worldwide web domains to start with "www", but doing so still has a practical benefit. If you want to point your name server to a content delivery network or a "serverless" server, it is much easier to add a CNAME for a subdomain than to switch the A record.

## German Idiosyncrasies

German grammar dictates to create new words out of compound nouns, so front-end development becomes "Frontendentwicklung" in German - unless when registering domain names. Germans and Japanese tend to favour domains that use a hyphen wherever possible. 

Apart from staying consistent with my established URL, [www.ingo-steinke.de](https://www.ingo-steinke.de/), separating given names and family names for better readability is one of the few valid reasons to use a hyphen in an international domain, so I decided to use [www.ingo-steinke.com](https://www.ingo-steinke.com/) to publish my English content.

## Google Search Console

Due to the increasing role of mobile usability for search engine optimization, Google's Lighthouse and Search Console try to help webmasters identify and fix issues found on their websites ... well, at least a little bit.

![Google search console screenshot showing the issue: clickable elements too close together](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/62cz6wb3sade4i2ttp12.png)

### Content Wider Than Screen?

Google was helpful enough to point out what kind of issue is found on which URL, but it would not tell me, what element caused the error. Fair enough, it often needs manual inspection, maybe even a little bit of intuition, to fix "Content wider than screen", but if they find some "clickable elements too close together" to be clicked conveniently on a mobile, they might just tell me the problematic elements.

## Perfectly Imperfect

Automated Tests using Cypress and Percy will hopefully make it safe to refactor my setup when I find some time.