Improving My Old Project: A Focus on User Experience
I am a big fan of frontend mentor’s challenges. My favorite thing about the concept is that each person can use the challenge as a means to learn aspects of frontend development that are relevant to them.
Inspired by this video on technical SEO for developers, I came back to my previously completed News homepage challenge in order to focus on an aspect that I've overlooked when building personal projects.
Check out the deployed version on Github pages.
As frontend developers, we are also responsible for ensuring that our sites are performant, accessible, and optimized for search engines. This not only enhances the user experience but also increases the visibility of our sites, ultimately leading to higher traffic and engagement.
For the purpose of this article, we’re going to focus on the user experience part of SEO, with Accessibility and Performance being a part of it. Google's PageSpeed Insights provides a comprehensive report on the user experience of a page on both mobile and desktop devices, offering suggestions for improvement. These metrics are among the many factors Google considers when ranking your website.
This article serves as a beginner’s checklist, a practical guide that can be immediately implemented into your existing projects to enhance these metrics.
Having said that, here are the changes I implemented to my News HomePage in order to get a perfect score of 100 on all categories of PageSpeed Insights:
Semantic HTML: A Foundation for Accessibility and SEO
Semantic HTML is the proper use of HTML to emphasize the meaning of content in web pages and web applications rather than simply defining its appearance.
For more information on this, I find Semantic HTML: What It Is and How to Use It Correctly to be a good introduction into the subject.
Or check out this article in the mdn documentation: Document and website structure
By using a logical hierarchy of headings
and key structural elements like <header>
,<main>
, and <footer>
, we can create a cascading hierarchy. This forms an accessibility tree that search engines use to comprehend the structure and content of a web page.
You can find the accessibility tree of a website in Developer Tools > Accessibility Tab on Firefox and under the Elements tab > Accessibility tab in Chrome. A good way to review it by trying to understand the purpose of the elements if you couldn’t perceive it visually.
A few changes that I made in this regard that I can highlight are:
1. Adding a visually hidden <h1>
to convey information about the site for screen readers and search engines;
The element must be visually hidden without using display: none
, because this would remove it from the accessibility tree. Read more on usage of h1 tag: Page Title - H1 - Best Practices.
Use this strategy with caution, because Google can find hidden text to be deceptive. This could potentially lead to your site being penalized by Google, but only if you use it unethically.
<header>
<h1 class="visually-hidden">Latest News in Technology</h1>
<img ... />
<nav> ... </nav>
</header>
.visually-hidden {
position: absolute;
left: -10000px;
width: 1px;
height: 1px;
overflow: hidden;
}
2. Consistent usage of a logical a hierarchy of headings
in articles;
This news homepage has articles in the sidebar as well as at the bottom of the page. Notice the consistent usage of h3
for article names, h4
for article description. Even though “02” is the largest text in the article, it is a simple span
due to lack of meaning.
There’s not a lot of strict rules around semantic HTML, but if you build a habit of thinking about the meaning of an element, you will start making better choices every time. Read more about heading elements.
<article class="section-item">
<img src="assets/images/image-top-laptops.jpg" alt="top-laptops" />
<div>
<span>02</span>
<h3><a href="#">Top 10 Laptops of 2022</a></h3>
<h4>Our best picks for various needs and budgets.</h4>
</div>
</article>
<article>
<h3><a href="#">The Downsides of AI Artistry </a></h3>
<h4>
What are the possible adverse effects of on-demand AI image generation?
</h4>
<hr />
</article>
3. Correct usage of links and buttons;
The central button that says: Read more is actually a link styled like a button. Its purpose is to take a user to a new location, where he can read the entire article. Buttons trigger an action, for example, toggle the menu from opened to closed, which we’re going to touch upon later.
4. Adding title
and desc
elements to provide text alternatives for SVG content;
<svg>
<title>Open Navigation Menu</title>
<desc>
Button that opens the menu containing navigation links
</desc>
…
</svg>
5. Correct usage of aria-
attributes;
aria-
attributes provide additional semantics about the role, state, and functionality of an element, which can be especially helpful for assistive technologies like screen readers. Let’s take a look at our toggle button.
Aria-label represents the name of this button that doesn’t have text. It is read by screen readers to help visually impaired users know the purpose of the button.
The aria-expanded indicates to assistive technology whether the controlled element (the menu) is expanded or collapsed.
<nav class="navigation-container">
<button
id="toggle-menu"
aria-expanded="false"
aria-label="toggle menu"
aria-controls="menu"
>
<svg>
...
</svg>
</button>
<ul id="menu">
<li><a href="#"> Home </a></li>
...
</ul>
</nav>
Optimize Images: Prioritizing Contentful Paint and Layout Stability
Image optimization is crucial for Core Web Vitals (the set of metrics that Google uses to measure the user experience of a webpage) for several reasons:
Impact on Largest Contentful Paint (LCP): Images can significantly impact the LCP score, which measures how long it takes for the largest element on the page to become visible to the user. If an image is enqueued too late or takes too long to load, it can significantly slow down the page’s LCP score.
Impact on Cumulative Layout Shift (CLS): Images can cause layout shifts if they are not properly sized and are inserted into the page after it has already loaded, causing other elements to move around. This can negatively impact the CLS score, which measures the visual stability of a page.
To address these issues, I implemented a few changes. Note that this is just stretching the surface of image optimization. You can find more information on other important topics such as image compression on the internet if you want to dive deeper.
1. Defining the width and height attributes of images;
<picture class="image">
<source
media="(min-width: 62em)"
srcset="assets/images/image-web-3-desktop.jpg"
width="850"
height="400"
/>
<img
src="assets/images/image-web-3-mobile.jpg"
alt="article-image"
width="480"
height="400"
/>
</picture>
While these values do not dictate the rendered size of the image, they assist the browser in reserving the appropriate amount of space for the image. You can still style the width
and height
with css.
This approach is effective when you have control over the image. In instances where you don't, such as when fetching from an API, alternative solutions exist. One such solution is to set the size of the parent container and contain the image within it.
.image-container {
position: relative;
}
.image {
width: 100%;
position: absolute;
}
These are similar to what Next.js uses with its Image component in order to prevent layout shifts.
2. Adding high priority preloading of the image that is the largest and appears first on the screen;
<link
rel="preload"
fetchpriority="high"
as="image"
href="assets/images/image-web-3-mobile.jpg"
type="image/jpg"
/>
I didn’t do this for this project, but you can also add lazy loading for the images that are not immediately displayed on the page.
3. Using alternative image formats;
While this project utilizes images in the .jpg format, alternative formats such as .avif
or .webp
can provide better quality photos when compressed and smaller file size when uncompressed.
Optimize Fonts: Tackling Layout Shifts and Load Times
Along with images, fonts are the culprits of layout shifts and prolonged load times, due to requests made to the Google Fonts API.
For this particular project, we were provided with the specific font name and weights required, enabling us to download the necessary .ttf
files and incorporate them into our static assets. This eliminates the need for server requests.
While this approach accelerates the process, a shift from the fallback font still occurs, which can even alter the text’s line arrangement.
To address this, I implemented a fallback font strategy. Arial, being a widely available font, is adjusted using several properties with the assistance of a Fallback Font Generator. This ensures layout consistency until the desired font is loaded.
@font-face {
font-family: "Inter";
src: url("../assets/fonts/Inter.ttf") format("truetype");
font-display: swap;
}
@font-face {
font-family: "Adjusted Arial Fallback";
src: local(Arial);
size-adjust: 109%;
ascent-override: 86%;
descent-override: normal;
line-gap-override: normal;
}
Still a bit of a layout shift, but much better than before :).
Efficient JavaScript: Reducing Overhead and Enhancing Performance
The News homepage project doesn’t demand extensive JavaScript coding. The only user interaction opportunity is toggling between expanded and collapsed states of a menu on a button click. We also need to handle image change when the screen size changes. Therefore, I won’t be elaborating much on the third Core Web Vital First Input Delay (FID) (or Interaction to Next Paint (INP) that will replace FID as a Core Web Vital in March 2024) and its optimization techniques. I encourage you to read through this web.dev article for more on this.
Despite the project’s simplicity, I still managed to write some inefficient JavaScript code to be later improved through refactoring.
The handleResize() function is not a function that I’m proud of writing, primarily due to its over-reliance on JavaScript for presentation logic that could be handled by CSS.
It is attached to the window’s resize event. This means it will be called every time the window is resized, which can trigger many times per second during a resize, leading to high CPU usage.
const handleResize = () => {
const articleImage = document.getElementById("article-image");
if (window.matchMedia("(max-width: 992px)").matches) {
menu.classList.add("display-none");
menu.classList.add("flex-direction-column");
toggleMenuButton.classList.remove("display-none");
articleImage.src = imageMobile;
} else {
menu.classList.remove("display-none");
menu.classList.remove("flex-direction-column");
toggleMenuButton.classList.add("display-none");
articleImage.src = imageDesktop;
}
};
window.addEventListener("resize", handleResize);
handleResize();
By utilizing the srcSet
attribute on images to manage screen resizing and implementing a more efficient CSS logic, I was able to completely eliminate the handleResize function. I now have a single event listener on the toggle button to manage clicks.
Instead of adding and removing the “display-none” and “flex-direction-column” classes depending on the screen size, the toggle() method on classList adds the “expanded” class if it doesn’t exist and removes it if it does and lets CSS handle the rest.
const toggleMobileMenu = () => {
navBar.classList.toggle("expanded");
backdrop.classList.toggle("backdrop");
if (navBar.classList.contains("expanded")) {
toggleMenuButton.setAttribute("aria-expanded", true);
return;
}
toggleMenuButton.setAttribute("aria-expanded", false);
};
document
.getElementById("toggle-menu")
.addEventListener("click", toggleMobileMenu);
While there are numerous scenarios where reducing JavaScript usage may not be straightforward, cultivating a habit of evaluating whether a task can be accomplished with less JavaScript is likely to enhance performance.
A Continuous Journey of Improvement
The use of Semantic HTML, image and font optimization, and JavaScript reduction are just a few strategies to improve the user experience of your website.
It’s crucial to continuously test, learn, and adapt your strategies based on your specific needs and goals. I encourage you to try these strategies on your personal projects, as you build habits that will serve real-life projects as well.
I hope you found this article helpful. As I share my knowledge with you, I am also learning myself. Therefore, your questions, comments, and suggestions are so greatly appreciated🙏 and will help improve future content. Thank you for taking the time to read and engage with this material. Happy coding!
Top comments (1)
Very impressive article, it covers a lot of essential frontend skills.
thanks for sharing.