Logo

Creating an Accessible, Responsive Pixel Perfect Website

cover image

Last post addressed the definition, benefits, and application of semantic HTML. In this post, I will walk through the process of coding an accessible, responsive website using what we've learned in the series.

Starting this series, we had the following design:

Website design with navbar and hero section

And we created a layout model using a technique learnt on the first post of the series:

Layout model of website design

Having this we can begin translating this model to HTML and CSS!

Responsive Design

One more thing before starting, we have to think about how we will make the site responsive. There are two ways we can achieve this:

  1. Set a max-width to all our sections and focus on the most used breakpoints.
  2. Make it fluid on all screens.

Note: Every design should aim to be responsive in all screen sizes. However, it can be really time-consuming, therefore we use one of the two methods above to avoid creating media queries for all screens available.

Selecting one of both methods greatly depends on the situation and preference.

Setting a max-width is what I normally go for. The design stays consistent on big screens and is easier to style since there are fewer breakpoints to worry about.

Making it fluid is better for some designs. If implemented well (using grid e.g. 12 column grid) it can be done really quickly and will be responsive in all breakpoints. However, its cost of implementation can be quite high.

For the sake of this tutorial, we will go with the first method. For the breakpoints, we'll be using the most common:

  • Mobile (640px)
  • Tablet (768px)
  • Tablet Landscape (1024px)
  • Laptop (1440px)
  • Really big screens (>= 2000px)

CSS Normalization

Whenever creating a website, writing CSS that is consistent in all browsers is a must. To guarantee this, we use something called CSS Normalization. Normalize.css is great for this, however, it is an overkill for this kind of project. Therefore, we will be using a very basic CSS Normalization, but I highly recommend Normalize.css.

In our basic normalizer, we will just remove browsers added margin and padding. Also, add box-sizing to guarantee a consistent width and height property.

Apart from removing browser's defaults, using rem units instead of px will streamline the process of making the site responsive since each element will grow or decrease based on the user's browser zoom or breakpoints. To achieve this, we will change font-size value to a percent. In this case, 62.5%, so that 1 rem would be equal to 10 pixels. On the other hand, whenever we need for the design to stay consistent we can use px units.

/*
  Breakpoints: 
    Mobile (640px = 40em)
    Tablet (768px = 48em)
    Tablet Landscape (1024px = 64em)
    Laptop (1440px = 90em)
    Really large screens ( >= 2000 = 125 em)
*/

@import url("https://fonts.googleapis.com/css?family=Fira+Sans:400,500&display=swap");

* {
  margin: 0;
  padding: 0;
  box-sizing: inherit;
}

html {
  font-size: 62.5%; /*1rem = 10px*/
  box-sizing: border-box;
}

@media only screen and (max-width: 64em) {
  html {
    font-size: 50%;
  }
}

/* For really big screens */
@media only screen and (min-width: 125em) {
  html {
    font-size: 75%;
  }
}

body {
  font-size: 1.4rem;
  font-family: "Fira Sans", sans-serif;
}

Navigation Bar

Let's start with the navbar. The navbar section element is header since it serves as our website's heading containing the site's main links and general brand.

Note: I will be using BEM notation for elements CSS classes since it increases legibility.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Pixel Perfect Layout</title>
  </head>
  <body>
    <header class="navigation">
      <svg       
        class="navigation__logo" 
        width="75"
        height="24"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      >
        <path
          d="M9 0C3.648 0 0 3.648 0 9c0 5.256 3.72 8.856 9 8.856 2.376 0 4.752-.48 7.008-1.68V7.488H9.504v2.736h3.48v4.008c-1.2.672-2.688.888-3.96.888-3.648 0-5.856-2.808-5.856-6.312C3.168 5.424 5.52 2.736 9 2.736c1.584 0 3.288.552 4.44 1.656l2.232-2.256C13.872.504 11.352 0 9 0zM24.664 5.616c-1.512 0-2.784.792-3.408 2.112h-.048V5.904h-2.88v11.52h2.88v-6c0-1.536.912-3.072 3.096-3.072.432 0 .84.072 1.392.216V5.784c-.384-.096-.6-.168-1.032-.168zM31.528 5.616c-2.256 0-3.864.792-4.896 1.848l1.512 1.512a4.577 4.577 0 013.072-1.2c1.584 0 2.64.792 2.64 2.136v.336h-.888c-4.944 0-7.08 1.344-7.08 4.008 0 2.112 1.824 3.456 4.2 3.456 1.536 0 2.88-.576 3.696-1.872h.072v1.584h2.592v-6.888c0-2.712-.984-4.92-4.92-4.92zm-2.76 8.424c0-1.224 1.536-1.776 4.08-1.776h.84v.72c0 1.464-.912 2.568-2.88 2.568-1.08 0-2.04-.528-2.04-1.512zM36.986 5.904l4.704 11.52h3.072l4.536-11.52h-2.952l-3.096 8.064h-.048L40.13 5.904h-3.144zM51.456.264c-1.032 0-1.824.792-1.824 1.728 0 .936.792 1.728 1.824 1.728s1.872-.696 1.872-1.728c0-.984-.816-1.728-1.872-1.728zm-1.416 5.64v11.52h2.88V5.904h-2.88zM56.258 2.568v3.336h-2.376v2.448h2.376v5.4c0 2.904 1.056 3.96 3.768 3.96.648 0 1.728-.12 2.28-.384v-2.424c-.24.192-.84.36-1.56.36-1.128 0-1.608-.576-1.608-1.8V8.352h3.168V5.904h-3.168V2.568h-2.88z"
          fill="#222"
        />
        <path
          d="M61.994 5.904l4.92 11.616-.504 1.248c-.528 1.272-.816 1.824-2.328 1.824-.504 0-1.008-.12-1.464-.288l-.36 2.592c.72.192 1.464.288 2.208.288 2.88 0 3.648-1.416 4.584-3.792l5.256-13.488h-3l-2.88 7.992h-.048l-3.216-7.992h-3.168z"
          fill="#222"
        />
      </svg>

      <span class="navigation__icon">
        <svg
          width="20"
          height="20"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path
            fill-rule="evenodd"
            clip-rule="evenodd"
            d="M0 8.5a8.5 8.5 0 1115.453 4.89l3.44 3.437a1 1 0 010 1.415l-.649.647a1 1 0 01-1.413 0l-3.44-3.437A8.5 8.5 0 010 8.5zm8.5 6.071A6.078 6.078 0 012.429 8.5 6.078 6.078 0 018.5 2.429 6.078 6.078 0 0114.571 8.5 6.078 6.078 0 018.5 14.571z"
            fill="#222"
          />
        </svg>
      </span>
      <button class="navigation__button">
        <svg
          width="34"
          height="14"
          fill="none"
          xmlns="http://www.w3.org/2000/svg"
        >
          <path fill="#222" d="M0 0h34v3H0zM16 11h18v3H16z" />
        </svg>
      </button>
     <nav class="navigation__links">
        <ul>
          <li><a href="/">Home</a>></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
      </nav>
    </header>
  </body>
</html>

Writing the styles for the navigation bar is pretty straightforward once you have the CSS purpose of each block. We can refer to our model:

Layout model of navigation bar

By taking a look, the block uses flex to organize its elements.

Also, it has some padding (more on its right and left than top and bottom). Let's translate this to CSS:

/* ... */ 

/* SECTION */
.navigation {
  display: flex;
  align-items: center;
  justify-content: space-between;

  padding: 5rem 9rem;

  max-width: 1440px;
  margin: 0 auto;
}

@media only screen and (max-width: 40em) {
  /* Reduce the padding on smaller screens */
  .navigation {
    padding: 5rem 3rem;
  }
}

/* ELEMENT */
.navigation__search {
  cursor: pointer;
}

/* ELEMENT */
.navigation__button {
  border: none;
  background: transparent;
  cursor: pointer;
  outline: none;
}

/* BLOCK */
.navigation__links {
  display: none;
}

Desktop navigation bar

Mobile navigation bar

That's all for our navigation bar!

Hero Section

Next, let write our hero. This time we'll use a section tag as the container since we are defining a group of related content.

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>Pixel Perfect Layout</title>
  </head>
  <body>
   <header><!-- ... --></header>
   <section class="hero">
      <img class="hero__video" src="assets/video.png" />
      <header class="hero__heading">
        <div class="hero__text-box">
          <h1 class="heading-primary">Work around you and your team</h1>
          <p class="hero__text text">
            From ads that dance or sing to MTV-like commercials, online
            advertisers are now using a new type of technology “rich media” to
            attract consumers.
          </p>
          <button class="hero__button btn btn--purple">get started</button>
        </div>
        <div class="hero__loader">
          <p>01</p>
          <progress value="40" max="100"></progress>
          <p>03</p>
        </div>
      </header>
      <img class="hero__image" src="assets/groove.png" />
    </section>
  </body>
</html>

You may see we are using header inside a section. It is perfectly normal to do this since it indicates that it is the introductory element of that section. Looking back at our navigation bar, it is the introductory element for our document or website.

We'll have something like this. Although semantic and accessible, it lacks beauty and responsiveness.

Hero section with no CSS applied

Styling our hero has a higher complexity than our navigation since it has more elements and blocks. Regardless, using our model we can achieve the pixel-perfect look we want.

It is important to take things one step at a time looking at each CSS purpose of our model. Likewise, some of the code will be trial and error; it is perfectly fine to do this as long as it gets to look as our intended design.

Layout model of hero section

/* ... */ 

/* SECTION */
.hero {
  display: flex;

  max-width: 1440px;
  margin: 0 auto;

  /*
    Since we have an unnatural block on this section
    we have to make our section relative for it to act
    as an anchor.
  */
  position: relative;
}

@media only screen and (max-width: 48em) {
  .hero {
    flex-direction: column;
  }
}

/* UNNATURAL */
.hero__video {
  position: absolute;

  top: 50%;
  left: 60%;
  transform: translate(-60%, 0);

  object-fit: contain;
  
  box-shadow: -20px 60px 120px rgba(0, 0, 0, 0.3);
}

@media only screen and (max-width: 90em) {
  .hero__video {
    width: 30vw;
  }
}

@media only screen and (max-width: 48em) {
  .hero__video {
    top: initial;

    bottom: -2%;
    left: 50%;

    transform: translate(-50%, 0);
  }
}

@media only screen and (max-width: 40em) {
  .hero__video {
    width: 30rem;
  }
}

/* BLOCK */
.hero__heading {
  flex: 1;

  display: flex;
  flex-direction: column;
  justify-content: space-around;

  padding: 3rem 9rem;
}

@media only screen and (max-width: 40em) {
  .hero__heading {
    padding: 3rem 4rem;
  }
}

/* BLOCK */
.hero__text-box {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
}

.hero__text {
  max-width: 450px;
}

.hero__text-box > *:not(:last-child) {
  margin-bottom: 5rem;
}

.hero__button {
  align-self: start;
  margin-top: 2rem;
}

/* ELEMENT */
.hero__image {
  width: 50%;
  object-fit: contain;
}

@media only screen and (max-width: 48em) {
  .hero__image {
    width: 100%;
    height: 50rem;
    object-fit: fill;
    padding: 2rem 9rem;
  }
}

@media only screen and (max-width: 40em) {
  .hero__image {
    padding: 2rem 4rem;
  }
}

/* BLOCK */
.hero__loader {
  display: flex;
  align-items: center;
  width: 100%;
  margin-top: 4rem;
}

.hero__loader > *:not(:last-child) {
  margin-right: 2rem;
}

/* ELEMENT */
.hero__loader p {
  font-size: 1.6rem;
}

/* ELEMENT */
.hero__loader progress {
  width: 20rem;
  height: 0.3rem;
}

.hero__loader progress[value] {
  /* Reset the default appearance */
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;

  /* Get rid of default border in Firefox. */
  border: none;
}

.hero__loader progress[value]::-webkit-progress-bar {
  background-color: #d7d4d4;
}

.hero__loader progress[value]::-webkit-progress-value {
  background: #c6b393;
}

/* Firefox */
.hero__loader progress[value]::-moz-progress-bar {
  background: #c6b393;
}

/* COMPONENTS */

.heading-primary {
  font-size: 6rem;

  color: #151515;
}

.text {
  font-size: 1.8rem;
  line-height: 28px;

  color: #222222;

  opacity: 0.7;
}

.btn {
  padding: 1.5rem 3rem;

  font-family: inherit;
  text-transform: uppercase;

  letter-spacing: 2px;
  font-weight: 500;

  border: none;
  cursor: pointer;
}

.btn--purple {
  background-color: #4737ff;
  color: white;
}

Now we have this!!

Final website looking exactly like mockup

And on mobile:

Note: The mockup didn't have a mobile version, regardless, this is a little extra.

Mobile version of final design

Here is the sandbox if you want to play around with the website.

Conlusion

Creating pixel-perfect layouts is very important for web developers since most websites have a mockup behind them. With layout models and semantic HTML, we can create almost any website with high fidelity while taking care of accessibility and responsiveness.

Thanks for reading this series on how to create Pixel Perfect Layouts 🥳! Feel free to check out the other parts. Follow me on Twitter, and Dev.to! See you soon 🔥💻


You may also like


Front-End novelty in your inbox

My goal is to create easy-to-understand, rich blog posts for Front-End Developers. If you love to learn more about streamlined front-end practices, you'll more likely love what I'm working on! Don't worry, there will not be spam, and you can unsubscribe at any time.

You will also receive awesome weekly resources to stay ahead in web development!

(required)
contactContact Me