Less is good. Moving a Legacy Web App to CSS Grid.
Tags: css, javascript, cssgrid, performance
Published: 2019-10-12 09:08:46

What do developers do on a weekend? Rest? Party? Hang out? Code?

For me last weekend, it was code :). I had built a web application for a client in 2013 and had to fix some issues after adding new features. It didn’t use bootstrap because it was still new then, so the app was styled using custom styles, Modernizr, etc. I had made a mental note to move the styling to bootstrap but in 6 years, I had not found the time to do this. However, I couldn’t stand the archaic interface anymore. So I decided to restyle the login page (at least that would be easy eh?).

The plan was to use bootstrap and be done quickly. I had other important things to work on. However, I wondered if a login page really needed network requests to load bootstrap and dependencies. I am not a fan of dependencies, so I decided there had to be a better way. I had heard rave reviews of CSS Grid but never tried it out. Well, isn’t the weekend a good time to try out new stuff? So I decided to restyle using CSS Grid.

The first thing I did was to look for a good CSS Grid learning material. This interactive course from freeCodeCamp was really useful (https://learn.freecodecamp.org/responsive-web-design/css-grid/). I loved being able to see changes in real-time. The only snag was I couldn’t see my progress in the course. After a few lessons, I had all I need to style my login page with CSS Grid.

The next step was to design a UI. Pressed for time, I went looking for inspiration on dribbble and found something simple (https://dribbble.com/shots/5482984-Admire-Login-Page)

I loved the simplicity and since this was an application staff would use daily, I loved the fact that I could use the right part of the page to display dynamic content e.g. event photos, staff photos, wallpapers, quotes, etc.

To use CSS grid we need to define our display property as grid on the containing element. This lets the browser know how to display content. We also want to split the page in half based on the design. This is easy using the grid-template-columns property which helps us define grid columns. fr means fractions. If we want the children of the containing element to have equal widths; we use 1fr 1fr

If we want a child to have more with than the other we can increase the fraction of the child. eg 1fr 1fr for 2 children is 1/2 1/2 in maths. If we had 3 children elements, we’d do 1fr 1fr 1fr. If we want the last element to take more space, we can do 1fr 1fr 2fr. The last element now takes twice the space of other elements.

.container { 
display: grid; 
grid-template-columns: 1fr 1fr; 
height: 100vh;
}
.page-half {
height: 100vh;
}
#left {
background-color: red
}
#right {
background-color: black;
}
<div class="container">
<div id="left" class="page-half"></div>
<div id="right" class="page-half"></div>
</div>
The code above splits the container element contents into two and sets the container element height to take the whole screen. We also set the child elements (left & right) to take the full height of the screen. We set background colors so we can see the result.
Let’s replace the background of the right part of the page with a lovely image our users would love to see daily.
#right {
background: url("../img/bgs/bg3.jpg");
background-size: cover;
background-repeat: no-repeat;
background-position: center;
}


You might notice some spacing at the outer edges of your container element. This is because of the default browser body margins. Let's remove that.

body {margin: 0;}

We can now add in our login form and related content to the left half of the page. What we want to do is split the left part into 3 rows with the middle row taking twice the space of other rows. We can use grids within grids :). So let’s define the left half as a grid and split it into rows. To split a grid into rows, we use grid-template-rowsWe also set horizontal paddings and give the rows a temporary border so we can see the result.
#left {
display: grid;
grid-template-rows: 1fr 2fr 1fr;
padding: 0 30px;
}
#left div {
border: 1px solid #000;
}
<div id="left" class="page-half">
<div></div>
<div></div>
<div></div>
</div>

We insert our logo, form and footer information. And also remove the temporary border.

<div id="left" class="page-half"> 
<header> 
<div id="logo"> <img src="http://nlyvn.com.ng/wp-content/uploads/2015/07/nlyvnlogo1.png" class="logo" /> 
</div> 
</header> 
<div class="login-form"> 
<h2>Welcome Back !</h2> 
<p>Sign in to access the Administrative Portal</p> 
<form> 
<div> 
<label>Email</label> 
<input type="text" name="email" id="email" autofocus required /> </div> 
<div> 
<label>Password</label> <label class="forgot-password"><a href="#">Forgot your password?</a></label> 
<input type="password" name="password" id="password" required /> </div> 
<div> 
<button name="submit" id="submit">Login</button> 
</div> 
</form> 
</div> 
<footer> 
<p>&copy; 2019 Fictional Company</p> <p class="author">Powered by NLYVN Media</p> 
</footer>
</div>


Let’s add some styling for our content. We want to give the logo some margin, style form fields and submit button, and align the footer information to the bottom of the page. To align content within a grid element, we use align-self for vertical alignment and justify-self for horizontal alignment. Some position values are start, center, stretch, end. For our footer, we’ll use end

header .logo { margin: 10px 0; } 
.login-form { width: 60%; } 
.login-form p { color: #888; margin-bottom: 30px; } 
.login-form label { display: inline-block; color: #444; } 
.login-form input { display: block; height: 40px; width: 100%; padding-left: 10px; font-size: 18px; margin-top: 15px; border-radius: 5px; border: 1px solid #ccc; box-sizing: border-box; } .login-form div { margin: 20px 0; } 
.login-form .forgot-password { float: right; }
.login-form .forgot-password a { color: #111; text-decoration: none; } 
.login-form button { height: 50px; width: 100%; border-radius: 5px; border: 1px solid #27ae60; background-color: #27ae60; color: #fff; font-size: 18px; padding: 0 25px; } 
.login-form button:hover { opacity: 0.9; color: #fff; } 
footer { align-self: end; } 
footer .author { font-size: 12px; }


We are done now eh? Not quite. We have not accounted for mobile displays/responsiveness. Sigh. Luckily, CSS grid supports responsiveness and its very easy to implement!

Responsiveness

We need to redefine our main container grid columns.

grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));

Now, we can see our columns are responsive. What changed?
The repeat helps us dynamically define multiple child elements column dimensions. It takes 2 parameters, the number of columns we want and the width. auto-fit helps with wrapping columns into a new row when they exceed the row width. minmax function lets us set what the minimum and maximum dimensions should be for child elements. In this case, we are telling the browser to use 320px as the min-width as the screen size reduces, while taking 1fr (full width) by default. Setting the min-width ensures our columns are not too small on mobile devices. Ire Aderinokun has a good post on minmax (https://bitsofco.de/how-the-minmax-function-works/). This article is also helpful in understanding repeat (https://css-tricks.com/auto-sizing-columns-css-grid-auto-fill-vs-auto-fit/)

Lastly, let’s hide the right half of the page on smaller screens and let form take up the whole width using media queries.

@media only screen and (max-width: 600px) { 
#right { display: none; } 
.login-form { width: 100%; } 
}

Performance

The former login page takes 10 seconds to load on slow 3G.

The new login page takes 12.42 seconds to load on slow 3G. This is largely because we are loading a high fidelity image (272kb) and google font libraries. However, DOM Content loads in 2.34 seconds which is impressive.
Can we improve on this? I believe we can. We could have a gradient background on the initial page load and load in the background image once the page loads. Let’s get some CSS gradient backgrounds from https://webgradients.com/ and write some JS to load in the background image! :)


With the CSS gradient background, Page loads in 6.45 seconds. Some improvement :). Keep in mind we are on slow 3G with caching disabled. Now let’s pull in the image background after the page loads.

#right {
  background: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%);
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}
<script type="text/javascript">
   window.onload = function () {
     const right = document.getElementById("right");
     right.style.backgroundImage ="url('./assets/img/bgs/bg3.jpg')";
   }
</script>

Yay! 7.12 seconds. I think we can call it a day :). But isn’t a static image every time going to be boring? Let’s add a random image function to give the user different images when they access the page. You might also notice a blank screen while the browser loads the image. We fix this by preloading the image, setting a temporary background color similar to the initial background and then replacing it with the new image once it has loaded.

#right {
  background: linear-gradient(to top, #a18cd1 0%, #fbc2eb 100%);
  background-color: #fbc2eb;
  -webkit-transition:opacity 0.5s ease-in-out;
  transition:opacity 0.5s ease-in-out;
  background-size: cover;
  background-repeat: no-repeat;
  background-position: center;
}
<script type="text/javascript">
window.onload = function () {
 function getRandomImage() {
  const images = ['./assets/img/bgs/bg3.jpg', './assets/img/bgs/bg2.jpg'];
  const randomIndex = Math.floor(Math.random() * images.length);
  return images[randomIndex];
 }
 const right = document.getElementById("right");
// load the image we want to replace the background with. helps to avoid a blank screen while browser loads image.
 let image = new Image();
        
 image.onload = function () {
  right.style.backgroundImage = "url(" + image.src + ")";
 }
 // get a random image
 image.src = getRandomImage();
}
</script>

That’s all folks!

Conclusion

CSS Grid helps us define layouts and helps us add responsiveness to web pages without the need for bulky libraries with dependencies like bootstrap. It helps with faster load times which helps with a great experience for your user. Try it today. Please share your thoughts and questions below.

Source code: https://gist.github.com/segebee/2c0cd58ffe03ffe4849bf740ca50d1f4
PS: Client name and related information were changed in this article.