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:
And we created a layout model using a technique learnt on the first post of the series:
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:
- Set a max-width to all our sections and focus on the most used breakpoints.
- 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:
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;
}
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.
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.
/* ... */
/* 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!!
And on mobile:
Note: The mockup didn't have a mobile version, regardless, this is a little extra.
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 🔥💻
Did you know I have a newsletter? 📬
If you want to get notified when I publish new blog posts and receive an awesome weekly resource to stay ahead in web development, head over to https://jfelix.info/newsletter.
Top comments (4)
Thank you it's very illuminating .
Thank you very much for the article, I learned a lot of new things
Thank you, I really appreciate your posts, I learn a lot from most!!
Super helpful 🙌 Thanks Jose