Improve performance, accessibility and SEO of your projects
Published: 2023-11-20 12:32:18

## 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](https://www.youtube.com/watch?v=BkDH55WDiDU), I came back to my previously completed [News homepage challenge](https://www.frontendmentor.io/challenges/news-homepage-H6SWTa1MFl) in order to focus on an aspect that I've overlooked when building personal projects.

![frontend mentor News Homepage challenge](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9zq88afrhizf43wm2bng.png)

Check out the deployed version [on Github pages](https://catalina-hasnas.github.io/news-homepage-main/).

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](https://pagespeed.web.dev/) 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](https://developers.google.com/search/docs/appearance/page-experience). 

![PageSpeed Insights](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hftzsb24o5nwnqyqnc5g.png)

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](#semantic-html-a-foundation-for-accessibility-and-seo)
  - [Image Optimization](#optimize-images-prioritizing-contentful-paint-and-layout-stability)
  - [Font Optimization](#optimize-fonts-tackling-layout-shifts-and-load-times)
  - [Javascript improvements](#efficient-javascript-reducing-overhead-and-enhancing-performance)

## 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.

![Semantic HTML meme with the bird](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3m7wp2htfkl3ocokiflz.png)

For more information on this, I find [Semantic HTML: What It Is and How to Use It Correctly](https://www.semrush.com/blog/semantic-html5-guide/) to be a good introduction into the subject.

Or check out this article in the mdn documentation: [Document and website structure](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/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.

![Accesibility tree of aside html tag](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pszbhe7a4woznk3jrq9d.png)

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](https://wdn.unl.edu/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](https://www.redefineyourmarketing.com/blog/hidden-text-seo-what-google-thinks).

```html
   <header>
      <h1 class="visually-hidden">Latest News in Technology</h1>
      <img ... />
      <nav> ... </nav>
   </header>
```

```css
   .visually-hidden {
     position: absolute;
     left: -10000px;
     width: 1px;
     height: 1px;
     overflow: hidden;
   }
```

### 2. Consistent usage of a logical a hierarchy of `headings` in articles;

![Headings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dxruk6vatd2yv295h9hh.jpg)

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](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Heading_Elements).

```html
   <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>
```

```html
   <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. 

![Links and Buttons](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cymi6fhcwc2070oal6yb.png)

### 4. Adding `title` and `desc` elements to provide text alternatives for SVG content;

```html
   <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.

```html
   <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](https://developers.google.com/search/docs/appearance/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.


![Layout shift](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vnxlz534yxhc2jh9jihk.gif)

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; 

```html
   <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](https://www.amitmerchant.com/prevent-image-layout-shift-using-this-simple-css-hack/).

```css
.image-container {
    position: relative;
}

.image {
        width: 100%;
        position: absolute;
    }
```
These are similar to what [Next.js](https://nextjs.org/docs/app/building-your-application/optimizing/images) uses with its Image component in order to prevent layout shifts. 

![layout stability with display absolute](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1cuasgn25k94mv56fmol.gif)

### 2. Adding high priority preloading of the image that is the largest and appears first on the screen;

```html
   <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](https://photutorial.com/image-format-comparison-statistics/).

## 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.

![Layout shift due to font load](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zwchtj6t0t22tx77i00q.gif)

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](screenspan.net/fallback). This ensures layout consistency until the desired font is loaded.

```css
@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;
}

```


![Layout stability after custom fallback font](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hwie15n6qcrq7ctun0st.gif)

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](https://web.dev/articles/fid) 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.

```js

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.

```js
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!