DEV Community

Jemima Abu
Jemima Abu

Posted on • Updated on • Originally published at blog.jemimaabu.com

How I Built My Perfect Score Portfolio Website

I recently published the fourth version of my portfolio jemimaabu.com.
With this version, I tried to make the code as performant and accessible as possible but still have an interactive site, so this article will be about how I managed to achieve that.

portfolio-designs-tiny.gif

These are the following features in my portfolio that we'll be discussing:

  • Clear content and easy to navigate.
  • Built with HTML, CSS and JavaScript (no frameworks or libraries)
  • Works on JS-disabled browsers
  • Vanilla JavaScript implementation of smart navigation and animate on scroll
  • 6 different color themes
  • Perfect scores on accessibility, performance, best practices and SEO

I shared the portfolio on Twitter once I was done and it got a lot of feedback and I also got messages from people asking about certain features so I decided to write this article to explain these features.

1. Creating The Design

It took me about a month to come up with the design for my portfolio, mostly because I designed the entire thing on a trial and error basis - swapping out different layouts and colors till I liked how it looked.

I also went around the internet looking at other people's portfolios for inspiration and using features that I liked.

These are some of the portfolios that I got inspiration from:

Clear content

A major reason I wanted to revamp my portfolio from the previous version was to have all my content on my site without overloading the landing page.

I had different pages for each content and chose designs I felt would best present the information:

  • About page - splitting sections into expandable details to reduce text appearance on page
  • Articles page - simple article title and description display according to how most blog posts are displayed
  • Talks page - information set in responsive table (the table converts to a list on smaller screens)
  • Projects page - projects set in slider carousel so I could include as many projects without having a terribly long page.

I also included selected parts of each content on the landing page along with a call-to-action button leading to each page to encourage user navigation.

2. Writing the code

Ever since the second version of my portfolio, which was easily the least performant version of all versions, I've always built my portfolios from scratch with HTML and CSS, using as little JavaScript dependency as possible.

I also try to avoid using frameworks or libraries so I can optimize my code for performance and accessibility.

Works on non-JS browsers

A lot of interactions on the page are actually done with CSS or semantic HTML and not JavaScript, such as:

The benefit of this is that the site isn't dependent on JavaScript for displaying information so it still works without it.

Vanilla JS Implementation

I used JavaScript to handle minor interactions on the page such as elements fading in on scroll, the smart navigation and handling multiple color themes. I also wrote two articles on how I did this:

In order to ensure that the content wasn't dependent on being displayed with JavaScript, I only implemented the effects on the elements once the JavaScript had loaded. For example, for the scroll-in element, my JavaScript code looked like this:

scrollElements.forEach((el) => {
  el.classList.add('js-opacity');
})
Enter fullscreen mode Exit fullscreen mode

Then I handled styling by targeting only the classes with the js-opacity attached.

.scroll-in.js-opacity {
  opacity: 0;
}
Enter fullscreen mode Exit fullscreen mode

This way the elements are only hidden if the script has actually loaded and since the landing page wasn't too heavy or making multiple calls, the script loads almost immediately.

How to implement multiple color themes

color-schemes.png
This was definitely the most fun feature to implement.

The first version of my portfolio has a dark/light theme switch but I wanted something more interesting this time so I thought, why have two colors when you can have six.

I implemented the multiple color theme feature using vanilla JavaScript and CSS.
The best part is, thanks to the implementation, I could have gone on to have as many color themes as I wanted.

This is the JavaScript code for handling theme switching:

const setTheme = (className) => {
  var root = document.getElementsByTagName('html')[0];
  root.className = className;
}
Enter fullscreen mode Exit fullscreen mode

All we need to do is assign a className to the root element depending on what's selected.
Then the HTML looks like this:

<button onclick="setTheme('light')" aria-label="Set light theme">
</button>
<button onclick="setTheme('dark')" aria-label="Set dark theme">
</button>
<button onclick="setTheme('brown')" aria-label="Set brown theme">
</button>
...
Enter fullscreen mode Exit fullscreen mode

And our CSS looks like this:

:root,
:root.light {
  --background-color: #fafafa;
  --text-color: #000;
  ...
}

:root.dark {
  --background-color: #0d1926;
  --text-color: #f7fafc;
  ...
}

:root.brown {
  --background-color: #eeeae2;
  --text-color: #120c07;
  ...
}
...
Enter fullscreen mode Exit fullscreen mode

Finally, make sure you only assign colors to each element according to the CSS variable.

body {
  background-color: var(--background-color);
  color: var(--text-color);
}
Enter fullscreen mode Exit fullscreen mode

An optional feature you can implement is setting a dark/light theme according to the user's system settings. We can do this using the prefers-color-scheme media query.
We just need to update our default root variables:

@media (prefers-color-scheme: dark) {
/* set new default root to same colors in root.dark */
  :root {
    --background-color: #0d1926;
    --text-color: #f7fafc;
  }
}
Enter fullscreen mode Exit fullscreen mode

That's pretty much the gist of the logic. You can see the complete implementation at my portfolio repository.

3. Deploying the site

Once I had all my content and logic implemented, I added the repository to my Netlify site and connected it to my custom site. You can read about how I set up continuous deployment from Netlify (and more options for hosting and deploying your site) in the third article building my portfolio inspired: How to Create a Portfolio Website – A Beginner Developer's Guide

How To Get A Perfect Lighthouse Score on Your Website

You can test the scores of your website using the Lighthouse audit tool in DevTools.

lighthouse-tab.png

It's a good idea to run Lighthouse in incognito mode to prevent extensions interfering with your score. It returns a report giving you a score according to how well you meet certain metrics and also tells you things you can fix.

You can easily increase your score by taking note of the returned items and making those fixes accordingly.

Here's how I ensured high scores in each category:

Accessibility

accessibility.png
It's always best to ensure your site is as accessible as possible and not just for score-related reasons. You can watch my video on Understanding Accessibility As A Concept for more information on that.

These are some of the things I do to ensure accessibility on my site:

  1. Using an accessibility checklist
  2. Writing code with the Web Accessibility extension in VSCode
  3. Using the appropriate aria-roles for custom elements
  4. Manually testing the site after building (tabbing through and checking that all elements get visible focus, mouse operable elements work with keyboards etc).

You can also use the WAVE tool for further testing after building.

Perfomance

perfomance.png
This definitely gave me the most headache but here's how I got a perfect performance score:

  • Compressed all images and included an explicit width and height. This is one of the most common contributing factors to decreased performance scores mostly cause it's a lot of stress to implement. I resized all my images to be the maximum width they'd be rendered on the page, around 720px and set explicit width and height and made them responsive with CSS.
<img src="" width="720" height="340" alt="">
Enter fullscreen mode Exit fullscreen mode
img {
  width: 100%;
  height: auto;
}
Enter fullscreen mode Exit fullscreen mode

I was able to do this because I served static images saved on my local folder so if you're calling your images from a CDN or somewhere else, look into the options they offer for rendering responsive images.

  • I split all my code according to the pages calling them to make sure I wouldn't be loading unnecessary stylesheets or scripts. My site was very CSS heavy so instead of calling one large CSS file, I split the files according to the pages that needed them.

file-structure.png

  • Reduced page load animation.
    Chrome detects how long it takes for text to appear on your page and the timing can affect your performance score so try to keep your loading animation under 1s (one second).

  • imported font using font-face instead of link. For some reason, Google Chrome marks downloading fonts from Google fonts as a render-blocking resource which is odd to me cause like aren't y'all part of the same company? You fix it, what do you want me to do (I'm kidding lol. Mostly).

SEO and Best Practices

best-practices.png
seo.png

SEO and Best Practices usually go hand in hand with performance and accessibility so as long as you have good scores there, they'll follow right along.
The audit report tells you what needs to be updated so you can always fix any errors that pop up.


Conclusion

And that's how I build my perfect score portfolio website. Maybe you're wondering why does it matter to have a perfect score on your website?

For bragging rights, obvy. Also, you get fireworks if you have a perfect score so that's always nice.

lighthouse-fireworks.gif

Seriously though, it's not the perfect score that matters, it's just the idea behind it.

You should always be building your sites to be as accessible and performant as possible, according to best practices and the SEO is an added benefit. Lighthouse scores are just a way of measuring how well you've done that.

Thanks for reading :) If you found this article helpful, let me know in the comments or on Twitter.

Top comments (14)

Collapse
 
mwrpwr profile image
Joseph Maurer

Cool website Jemima! Doing multiple color themes was a cool idea. Have you thought about having a different theme based on the system preference? The fade in animations are cool but are a bit slow when trying to scroll the page fast.

Collapse
 
jemimaabu profile image
Jemima Abu

Thanks :)

Yeah, I'm currently setting the default color theme using the prefers-color-scheme media query so it's based on the system preference, unless you select a different color scheme which is then stored in local storage.

The fade-in animations are set in a throttle function of 250ms to reduce the number of times the function is called while scrolling.

Hope that clears things up :)

Collapse
 
nibtime profile image
nibtime

Hello Jemina. Your website is very nice. Also, this article about your design process is very well written. Especially the parts about a11y are very useful to me, so thank you and great job! 👍

On your optimization arguments: I totally share your thinking there about minimalism and staying out of a framework (Vanilla JS). But a recent experience thought me a bit otherwise. In Lighthouse Chrome, a page of mine hat a perfect 100 in performance, and then I tested the same page on web.dev/measure/ and it was just 60-70 😲

With Gatsby 3 and Next.JS 10, I can get in the 90-100 there, but I have actually no real clue how to get there myself 😅. It has to be optimized SSG with build+asset pipeline + image optimization capabilities of those frameworks that make it happen. There are just so many mistakes to make if you have to reinvent those things yourself.

Next Analytics additionally gives you this thing "Real Experience Score", that tracks performance metrics of your real visitors over time. Nothing can beat real data like that and setting up something like this yourself is very hard and a lot of work.

Lighthouse also changed weights (web.dev/performance-scoring/) recently, so the local Lighthouse can't be assumed to be stable across Chrome versions.

This all shifted my opinion from "Stay unopinionated to optimize myself" towards "those things are way too much for me in reality and subject to frequent change, I probably shouldn't optimize myself at all and let frameworks do it". Also, a framework like Next.JS gets real-world feedback from companies like Netflix, Hulu, Twitch, and many others and can make things better based on that. It is just very hard to beat all the resources that flow into such frameworks with your own smarts.

I only use local Lighthouse to get in the right direction during development, but as a final argument about site performance in terms of user experience, it's far from enough in my opinion. You are right that just the idea behind the score matters (which is true for every single metric that exists) which is great UX, but achieving it for real is something very hard.

On a personal portfolio site, it probably doesn't matter that much though, and might be overkill. But maybe those considerations can be helpful for other projects you build in the future.

Collapse
 
jemimaabu profile image
Jemima Abu

Thanks for the feedback :D

Yeah I was trying to keep things simple with my portfolio but I'll definitely look into your suggestions for my other projects.

All the best :)

Collapse
 
pavelloz profile image
Paweł Kowalski

I highly recommend switching to mobile check on lighthouse, it will show more feedback on the page:

googlechrome.github.io/lighthouse/...

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
manssorr profile image
Mansour Koura

Hello Jemima, I like all your portfolio versions especially the last one, It's amazing !!
I have two comments,

  1. I would like the theme icon when displayed when hovering on it, without clicking.
  2. I like it if the Nav-bar moves with me to the page bottom. That's all, Thanks
Collapse
 
thuhtetdev profile image
Thu Htet Tun

I have visited your website.
It is awesome Jemima. I love it

Collapse
 
vasilisplavos profile image
Vasilis Plavos

Great job, congats 👏👏

Collapse
 
banjoanton profile image
Anton Ödman

Great content! I read some of your other articles as well, amazingly well written and very helpful. Thank you! 👏

Collapse
 
jemimaabu profile image
Jemima Abu

Thanks, glad you enjoyed it :)

Collapse
 
espoir profile image
Espoir Murhabazi

Not a front-end developer, but I like the blue color combination on your portfolio.

It is definitely something I will steal for mine...

Collapse
 
jankapunkt profile image
Jan Küster

+1 for Vanilla JS 🤩