Lessons learned from building landing pages

toluagboola

Toluwanimi Isaiah

Posted on June 8, 2021

Lessons learned from building landing pages

I recently took the time to improve my portfolio page, and I also had an opportunity to build a landing page for an open source project. This gave me the opportunity to learn a CSS framework, and pick up a few other useful skills along the way. I was able to build and deploy both sites, and I'm now writing about the lessons I learned during the course of working on both pages.

Choosing a framework

I made use of the Bulma framework for both projects. I chose Bulma because it's based on Flexbox, has no JavaScript (which I didn't need for either website) and is built with Sass which I wanted to learn. The class names in Bulma are also easy to understand and remember. It was amazing how much faster I was able to go with a framework. When I built my old portfolio page, I wrote the styles from start to finish and it took so long, but this time I didn't even spend up to half the time with much better results.

My portfolio page

One issue I had at first was that whenever I wrote custom styles, the framework styles would overwrite them. As a result, I found myself using !important a lot. I later realised that it was because of the order in which I linked them in my HTML. In CSS, when rules on the same element have the same specificity (classes in my case), the styles that come last will take precedence. To solve my problem and stop fighting with the framework, all I did was link my CSS file after the Bulma stylesheet, like so:

<link
    rel="stylesheet"
    href="https://cdn.jsdelivr.net/npm/bulma@0.9.2/css/bulma.min.css"
/>
<link rel="stylesheet" type="text/css" href="main.css" />
Enter fullscreen mode Exit fullscreen mode

As I progressed with my work, I realized that I was including the entire Bulma framework in my project, but I only used a small part of it for the page which didn't feel right to me. I did some research and discovered that Sass provided a much better way for me to include only what I needed from the framework. First I had to install bulma through npm:

npm install bulma
Enter fullscreen mode Exit fullscreen mode

This command created a bulma folder within the node_modules folder in my project directory. I was then able to remove the CDN link from my HTML file and import the Bulma components I needed using the path to their specific files:

@import "node_modules/bulma/sass/components/navbar.sass";
@import "node_modules/bulma/sass/elements/button.sass";
@import "node_modules/bulma/sass/elements/container.sass";
@import "node_modules/bulma/sass/elements/image.sass";
// ...and any other part of the framework I need
Enter fullscreen mode Exit fullscreen mode

Doing this reduced the size of the CSS bundle in one of the projects from 240KB to 160KB. Minifying the code reduced the file size further to 137KB which should translate to faster loading times.

Loading images

Whenever the pages were loading, I noticed that the images were causing the pages to shift because spaces were not reserved for them until they load which was not ideal. The solution was to set the width and height attributes for each image on the page so as maintain the layout of the page regardless of whether the images are loaded or not. With these attributes in place, layout shifting is prevented causing the page to behave more predictably.

<img
    loading="lazy"
    width="1500"
    height="975"
    src="image.png"
    class="project-img"
/>
Enter fullscreen mode Exit fullscreen mode

Finding out the specific dimensions of an image can be done through the browser DevTools. All you need to do is hover the cursor on the image's src link and a popup will provide more details. When doing this, it's important to use the intrinsic size which is the actual size of the image and not the rendered size which is dependent on the device.

Image showing intrinsic and rendered image widths

Another way I was able to improve the image loading experience was by lazy loading them. That means instructing the browser to delay the loading of off-screen images until the user scrolls near them. To achieve that, set the loading attribute on the img tag to lazy and that's it.

<img src="image.png" loading="lazy" />
Enter fullscreen mode Exit fullscreen mode

Learning Sass and CSS variables

To up my styling skills a notch, I decided to learn how to use a CSS Preprocessor (SASS) and more advanced CSS features like variables (a.k.a. custom properties). I chose to learn Sass simply because it's the most popular one out there. It can be installed with this command:

npm install -g sass
Enter fullscreen mode Exit fullscreen mode

To run Sass code in a web browser, it must first be transpiled to CSS. Every time a change is made in a styles.scss for example, the following command will compile it into styles.css which the browser can read. Link the compiled CSS file in your HTML file, and you're good to go.

sass --watch style.scss style.css
// Press `Ctrl + C` to cancel the operation
Enter fullscreen mode Exit fullscreen mode

With Sass, I was able to make use of mixins and nesting. For example, I had several flex containers in my application. Instead of declaring the same styles every single time, I created a mixin for it. The advantage is that it helped to reduce repetition, and kept my code cleaner and shorter. I also made use of CSS variables over Sass variables for the sole reason of being able to access and manipulate them with JavaScript if necessary.

:root {
    --bg-color: #ffffff; // CSS variable
}

// Mixin declaration
@mixin flex-container {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.project {
    background: var(--bg-color); // CSS variable in use
    @include flex-container; // Mixin in use
}
Enter fullscreen mode Exit fullscreen mode

Performance optimisation

Before deploying the sites to GitHub pages, I ran both of them through Lighthouse to measure their performance. Lighthouse provides audit reports in several categories such as Accessibility, SEO etc. Here are the initial results for one of the sites:

First results

I followed some suggestions from the report which include:

  • Adding alt attributes to all images.
  • Sizing images properly with the width and height attributes.
  • Eliminating render-blocking resources.

After improving my code based on the above recommendations and running the performance test again, my score on Accessibility went up from 75% to 94%. The remaining categories also went up in score:

New results

While I was reading up on render-blocking resources, I realised that more than 90% of the CSS on my page was not being used despite my attempts to include only what I needed from Bulma. This insight was gained when I checked the Coverage tab in the Chrome DevTools.

In the process of trying to figure out how to remove the unused code, I found out about UnCSS, a tool that removes unused CSS from your stylesheets. It can be installed by running:

npm install uncss --save-dev
Enter fullscreen mode Exit fullscreen mode

Then I created an uncss.js file and used the following code from the documentation:

const uncss = require("uncss");
const fs = require("fs");

// Your html files here
const files = ["index.html"];

uncss(files, (err, output) => {
    try {
        if (err) throw err;

        // Write output to a css file here
        fs.writeFile("main.css", output, (err) => {
            if (err) throw err;
            console.log("uncss: done!");
        });
    } catch (err) {
        console.error(err);
    }
});
Enter fullscreen mode Exit fullscreen mode

UnCSS analyses each HTML file in the files array and provides the trimmed css in the callback function (as output). fs.WriteFile then creates a main.css file and appends the content of the output. The whole operation is wrapped in a try...catch block for efficient error handling. After saving the file, I updated the build script in my package.json file:

"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "npx sass --style compressed main.scss main.css && node uncss.js"
},
Enter fullscreen mode Exit fullscreen mode

When I run npm run build, it converts the Sass file to minified CSS code, and subsequently runs the UnCSS script to remove the unused parts of my code thereby increasing performance and reducing loading time. It absolutely changed the game for me after I used it. The size of the CSS file in one of the projects went from 137KB to 11KB representing approximately a 2,000% reduction from the original 240KB stylesheet size without any visual differences! This ensures that I'm sending the smallest possible bundle to the users.

Coverage tab in Chrome DevTools

Takeaway

All in all, I learned a lot of new things during the process of building the both landing pages. They include:

  • The value of CSS variables.
  • Sass, its features and how to use them.
  • Using only what is needed to increase performance.
  • How to use the UnCSS tool to remove unused CSS code.
  • Setting specific width and height for images to prevent layout shifting.
  • Using Lighthouse to measure and improve site performance.

You can check and compare the old and new versions of the sites:

I hope you have gained some value from this article. Let me know your thoughts in the comments.

Thanks a lot for reading!

💖 💪 🙅 🚩
toluagboola
Toluwanimi Isaiah

Posted on June 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related