Design Tutorial, Part 2: Building a Responsive Dashboard Layout with Abstract, Sketch, CSS Grid & React

Part 2 of a 2-Part Design Tech Tutorial

By    Peter Vogt    Experience Designer projekt202

By Peter Vogt
Experience Designer
projekt202

Posted Jan. 17, 2019

This is the second part of a two-part tutorial in a writing series I call Design Tech. The purpose of the Design Tech writing series is to take a very focused look at working in the view layer as designers with both design and code.

Additionally, the goal of this series is to showcase some emerging design tools and technologies.

Part one can be found here.

In this post, I'll go over:

-Installing NodeJS, npm, and React locally on your computer

-Creating a local React project for prototyping your design with create-react-app

-Building a navigation component with styled-components and Flexbox

-Using media queries and CSS Grid Layout techniques to make the dashboard layout responsive in your browser

Section 1, Node/NPM/Create-React-App: Development Environment Setup

For this project, we're going to be building with prototyping in the browser in mind. We won't be handling any sort of dynamic data or user authentication. The main purpose of this tutorial is to get designers spinning up a front-end project on their local machine.

What is Node(JS)/NPM?

Node is a Javascript runtime that lets developers run scripts on the client and server side. It's a versatile way to create, maintain and add to a development project. It is massively adopted and is even the basis for many widely-used web frameworks today.

Probably most importantly in the context of UI, NodeJS allows the developer to install packages through npm or the Node Package Manager. Packages can be just about anything you'd like to add to your application, but for designers, you can think of it almost like Sketch's plugin manager. Just by typing a line or two in your computer's command line terminal, you can add an entire UI library like Bootstrap in seconds to your web app.

Let's get rolling by installing NodeJS on your machine if you haven't already.

Open up the NodeJS download page, and select the recommended build for your operating system.
Install Node VIA the wizard, and go ahead and open up your terminal.
If you're on a Mac, which I assume you are as a designer, you can run spotlight and search "Terminal", or check out this post which goes a little bit more in-depth on the terminal and how to use it.

NPM and NPX, which we'll use to create our React project, are included in this install. To verify this, run

npm --version

You should see the number of the latest version of NPM returned in the terminal. If you are returning "command not found," Node did not install correctly, and you'll have to do some troubleshooting. Thankfully, the Node community is vast and well-documented in places like Stack Overflow.

On to Create-React-App

Most people reading this will already be familiar with React. If you are not a developer, one thing you may not know about React is that configuring a React project can come with lots of overhead configuration. If you're merely looking to design or prototype in the browser, setting up a React project on its own can be a nightmare.

Create-React-App was made to solve this specific problem. The React team put this library together to give you everything you need for a development environment right out of the box. Go ahead and pull up your terminal, and enter:

npx create-react-app designtech

It'll take a minute or so, but you are now installing Create-React-App on your computer. Easy, huh? The terminal isn't so scary after all.

Let's run some more terminal commands and keep our momentum going. At the command line, using "documents" as whatever folder on your computer you'd like to set up your development project, enter:

cd documents
mkdir dev-projects

You've now changed directories into your "documents" folder, and made a directory called "dev-projects". This is exactly the same as clicking "documents" in the Mac finder, and creating a new folder called "dev-projects."

Now you have to tell the terminal to actually go to the directory you just created, so go ahead and enter

cd dev-projects

Now, enter:

npx create-react-app designtech

This command tells Create-React-App to *create* a React project called designtech in your dev-projects directory, using NPX. You can read more about NPX here, but the relevancy to the overall scope of this tutorial is low. It is, however, a good practice to understand the tools you're using as best as you can.

OK, great! After Create-React-App is done in the terminal, go ahead and run:

cd designtech
npm start

After a few seconds and some Node magic, your browser will point to http://localhost:3000, and you magically now have a React app running in your browser.

Local-wut?

Here's the breakdown of what you're currently doing - you are running a local server generated for you by Node and Create-React-App. And you did this with just a few terminal commands. Great job, designer! It may seem trivial, but this is actually a big step in learning.

You are now simulating how an application runs on a real server - one that would be hosted in a giant server farm in some place like Utah, or something. The purpose of this is to give you a development environment to write code in, and the ease of setup is what Node and Create-React-App bring to the table.

tech1.png

So how do I make it …. do stuff?

Currently on localhost, you'll see the out-of-the-box default UI for Create-React-App. The spinning logo is cool, but obviously this is not what we are trying to build.

Here's how you start customizing your freshly generated React app.

For the next step, you'll need a development-focused text editor. I like VSCode by Microsoft, but Atom, Sublime Text, or a JetBrains IDE are all suitable. Do some research and see what you like, but don't spend a lot of time worrying about it. Pick whatever one feels good to you and move on.

That's another point to drive home. Don't overthink anything in this tutorial, no matter how intimidating it may seem if you're leaving your designer-hat comfort zone. We're fingerpainting here. Have some fun once in a while. Life ain't so serious.

Navigate to your designtech folder in the Finder, and open the entire folder in your text editor. On VSCode, for example, you can simply drag the designtech folder to the VSCode icon on your Dock. VSCode will automatically open all of the files for the entire Create-React-App generated project.

Open the src directory, and open app.js. This serves as the "homepage" of your app.

tech2.png

Highlight everything between <div className="app"> and the last </div> in the block of code here and delete it. Return to your browser, and you'll now see an empty page. Changes are generating on the fly, which is great. You now have an empty page though, which is less great. Let's add some things.

Layout and Grid: Beginning with the End in Mind

I'm going to go back now to look at the design work in Abstract that I created in part one of this tutorial. The purpose is to see what type of layout I'll be building, and how I'll build it.

We have three breakpoints in this dashboard - Mobile, Tablet and Desktop.

large layout2.png
medium layout3.png
small layout4.png

The mockups show a fixed left sidebar that takes 100% of the height of the viewport, a fixed header in the right-side container (we'll call this the "content container"), two equal-width modules beneath that header, and a full-width module beneath that.

Let's keep that in the back of our heads, and start by installing the project's correct font, which is Roboto.

Go to the Google Fonts page for Roboto, hit "Select this Font," and open the drawer in the bottom right. Click the Customize tab inside the drawer, and select Light, Regular, Semi-bold and Bold.

Click back to the Embed tab, and click the @import link.

Copy everything inside the <style></style> tags. It should look like this:

@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,600,700');

Go back to your text editor, open "App.css" in your src directory, and paste the above @import line at the very top.

You are now importing Open Sans into the project.

First, the sidebar: styled-components

To begin with, we are going to build the fixed sidebar on the left.

Pull up your terminal again. If you've navigated away from designtech, navigate back to it using the cd command.

We are now going to install a dependency, which is a library that is not included in your project by default, but one you wish to add for whatever reason. Maybe it's something like Bootstrap, or maybe it's a package that helps you serve up a drag and drop component like react-beautiful-dnd. Either way, dependencies are libraries your project depends on to work the way you want them to.

Styled-components is a library that allows you to style your components with CSS in your React component files. It's part of a movement called, quite literally, "CSS-in-JS," named for the fact that you're writing CSS in Javascript files.

The purpose behind this largely is to keep all of your style inside the component file rather than writing a stylesheet separately and linking to it. I think if you are a designer, you'll find this conceptually familiar.

In the terminal while in the designtech directory, run the command:

npm install --save styled-components

You are telling Node Package Manager to grab the "styled-components" package and install it into your project.

Open up your text editor, which should still have the designtech project open, and navigate to the designtech/src directory. Create a new directory called "components" and create a folder called "sidebar" inside that. Then create a file called sidebar.js inside the sidebar directory.

First, in sidebar.js, write these two lines at the top (or copy and paste them from here):

import React, { Component } from "react";
import styled from "styled-components";

We've now told the project that this file is a React component and that we want to use styled-components with it.

Next, you have to include the mandatory class designation, render function and export that React requires of your components. If you look back at app.js, you really just need the same block here.

Sidebar.js

import React, { Component } from "react";
import styled from "styled-components";
class Sidebar extends Component {
render() {
return <div />;
       }
}
export default Sidebar;

Time to use styled-components. Under your imports at the top, enter

const SidebarContainer = styled.div``

Note that the two characters at the end are backticks, and not single quotes. Backticks are located next to the 1 key on most QWERTY keyboards.

Here, you're saying "Create a styled-components div called SidebarContainer." Inside your render function, replace the <div/> with:

<SidebarContainer></SidebarContainer>

The div is present in the DOM now, but you can't see it, because it has no style or properties to it.

Add initial styles to the SidebarContainer inside its backticks (referred to as its "template literal"), like so

const SidebarContainer = styled.div`
height: 100vh;
width: 270px;
background-color: #252529;
color: #fff;
`;

Hard-code a width of 270px for now, which is what the mockup has the desktop sidebar's width as. This will change when we add the rest of our components and construct the overall layout with CSS Grid.

Go back to http://localhost:3000 in your browser. What do you see?

That's right, nothing. This is because we need to now import  your Sidebar component into app.js, which, if you recall, serves as the homepage of your app. Fire up app.js again in your text editor, and add an import by telling the browser "Import my Sidebar from my file, sidebar.js", like so:

import Sidebar from "./sidebar";

Now, inside <div className="App"></div>, add just this:

<Sidebar />

App.js

import React, { Component } from "react";
import Sidebar from "./sidebar";
import "./App.css";
class App extends Component {
       render() {
              return (
                      <div className="App">
                             <Sidebar />
                      </div>
                      );
              }
      }
export default App;

Open http://localhost:3000.

:O!

Your component! It's not much, but it's there. Look at you go. If you're like me, particularly when I was learning to write code at first as a designer, you probably just got a small dopamine hit. Keep chasing that down, if so.

You may also be bored of all this by now and are teetering on the precipice of closing this article, too, which is also fine, even if I will consider it a personal insult (and I will).

Onward, All Ye Children of the SidebarContainer

For this to even resemble a sidebar that one might want to use in real life, we must add more to it.

Create a styled.ul called SidebarMenu underneath your styled.div, SidebarContainer. Just like SidebarContainer is a traditional HTML div, SidebarMenu will be a traditional unordered list (ul).

Also, add the properties "display: flex;" , "flex-direction: column", and "align-items: left;" to SidebarContainer.

Sidebar.js

const SidebarContainer = styled.div`
        display: flex;
        flex-direction: column;
        align-items: left;
        height: 100vh;
        width: 270px;
        background-color: #252529;
        color: #fff;
`;
const SidebarMenu = styled.ul`
        display: flex;
        align-items: center;
        flex-direction: column;
        list-style: none;
        width: 100%;
`;

Flexbox: A Brief Aside

You notice we added some new properties to SidebarContainer and to SidebarMenu as well. We are now declaring both of these elements as "flex containers," which is part of the CSS Flexbox paradigm.

The idea behind Flexbox is to provide a way to create pieces of UI that respond dynamically to the space inside and around them. You create Flexbox "containers" with display: flex, and use properties like "align-items" and "justify-content" to distribute, re-order, and space the children inside the flex "container."

It's a fairly deep topic, but one that designers should be able to grasp after only a little time studying. I recommend this article with helpful diagrams from CSS-Tricks. I keep it open as a reference basically any time I'm working with Flex, because it's just that helpful.

Some More Styled Items

Add a styled.li const called MenuItem,  styled.svg const called Icon, and a styled.p called SidebarMenuItemLabel. Style them per the mockups with CSS, like so:

const SidebarMenuItem = styled.li`
        width: 100%;
`;
const Icon = styled.svg`
        width: 20px;
        height: 20px;
`
const SidebarMenuItemLabel = styled.p`
        margin-top: 0;
        font-family: 'Open Sans', sans-serif;
        color: #fff;
        font-size: 14px;
        line-height: 1.5;
        font-weight: 500;
        color: #ffffff;
`

We now need to nest these menu items inside the menu ul, inside the render function near the bottom of your Sidebar.js file.

Our design calls for 6 menu items in the nav (not including the branding at top or the sign out at the bottom). We'll need 6 instances of SidebarMenuItem, each with their own Icon and Label.

At this point, your sidebar.js file should look like this:

import React, { Component } from "react";
import styled from "styled-components";
const SidebarContainer = styled.div`
        display: flex;
        flex-direction: column;
        height: 100vh;
        width: 270px;
        background-color: #252529;
        color: #fff;
        font-family: 'Open Sans', sans-serif;
`;
const SidebarMenu = styled.ul`
        display: flex;
        align-items: left;
        flex-direction: column;
        list-style: none;
        width: 100%;
`;
const SidebarMenuItem = styled.li`
        height: 40px;
        width: 100%;
`;
const Icon = styled.svg`
        width: 20px;
        height: 20px;
`;
const SidebarMenuItemLabel = styled.p`
        font-family: "Open Sans", sans-serif;
        color: #fff;
        font-size: 14px;
        line-height: 1.5;
        font-weight: 500;
        text-align: left;
        color: #ffffff;
`;
class Sidebar extends Component {
        render() {
             return (
                   <SidebarContainer>
                        <SidebarMenu>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Dashboard</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Service Alerts</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Customer Tickets</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Archive</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Library</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                              <SidebarMenuItem>
                              <Icon></Icon>
                              <SidebarMenuItemLabel>Deliveries</SidebarMenuItemLabel>
                              </SidebarMenuItem>
                        </SidebarMenu>
                 </SidebarContainer>
         );
    }
}
export default Sidebar;

At http://localhost:3000, you should see something like this:

Sidebar in browser6.png

Looking good so far. Let's add the rest of the styling for our SidebarMenuItems.

SidebarMenuItems7.png

Looking at the desktop mockup for the SidebarMenuItems, it appears as though there's an active state for the selected dashboard. Based on this mockup, I'm unclear if that active state is also the hover state, so I'm going to go have a conversation with my designer like a good little agile software person.

OK, the designer, who happens to be me, indicated that this is, in fact, the expected hover state as well. Good to go then. For good measure, I'll add "cursor: pointer;" to the hover effect on the parent SidebarMenuItem. It is a common usability pattern to leverage different browser cursors to indicate element interactions.

sidebar hover8.png

Looking … OK, I guess, so far, but the content is inset on the left and the Label text is pinned at the top of the SidebarMenuItem, which is not what we want. The font also needs to be Medium weight, which is 600.

Add "padding: 0px 30px;" to SidebarMenu, the flex parent of the SidebarMenuItems. Then, add "padding: 12px 0px;" to the SidebarMenuItemLabel, and give them a font-weight of 600.

Sidebar.js

const SidebarMenu = styled.ul`
        display: flex;
        align-items: left;
        flex-direction: column;
        list-style: none;
        width: 100%;
        padding: 0px 30px;
`;
const SidebarMenuItem = styled.li`

        height: 40px;
        width: 100%;
        &:hover {
        background: rgba(255, 255, 255, 0.05);
        box-shadow: inset 3px 0 0 0 #ffffff;
        cursor: pointer;
}
`;
const SidebarMenuItemLabel = styled.p`
        font-family: "Roboto", sans-serif;
        color: #fff;
        font-size: 14px;
        font-weight: 600;
        line-height: 1.3;
        text-align: left;
        padding: 12px 0px;
        color: #ffffff;
`;

Implementing Our Icons

Working with SVGs can be an opinionated issue. I'm not going to go down that path here. For purposes of this prototype, we can simply do whatever we want, so long as the end result looks as expected.

Sketch has a built-in "Copy SVG" capability, which is helpful here.

I can right-click on the dashboard icon in Sketch and select "Copy SVG Attributes", then paste the values into my text editor. We'll have to do some paring down of what Sketch is spitting out for us, into what you see below:

Sidebar.js

<SidebarMenuItem>
        <Icon viewBox="0 0 20 20">
        <path
        width="20px"
        height="20px"
        fill="white"
        d="M18,17 C18,17.552 17.552,18 17,18 L14,18 C13.448,18 13,17.552 13,17 L13,14 C13,13.448 13.448,13 14,13 L17,13 C17.552,13 18,13.448 18,14 L18,17 Z M18,11 L13,11 C11.895,11 11,11.895 11,13 L11,18 C11,19.105 11.895,20 13,20 L18,20 C19.105,20 20,19.105 20,18 L20,13 C20,11.895 19.105,11 18,11 L18,11 Z M18,6 C18,6.552 17.552,7 17,7 L14,7 C13.448,7 13,6.552 13,6 L13,3 C13,2.448 13.448,2 14,2 L17,2 C17.552,2 18,2.448 18,3 L18,6 Z M18,0 L13,0 C11.895,0 11,0.895 11,2 L11,7 C11,8.105 11.895,9 13,9 L18,9 C19.105,9 20,8.105 20,7 L20,2 C20,0.895 19.105,0 18,0 L18,0 Z M7,17 C7,17.552 6.552,18 6,18 L3,18 C2.448,18 2,17.552 2,17 L2,3 C2,2.448 2.448,2 3,2 L6,2 C6.552,2 7,2.448 7,3 L7,17 Z M7,0 L2,0 C0.895,0 0,0.895 0,2 L0,18 C0,19.105 0.895,20 2,20 L7,20 C8.105,20 9,19.105 9,18 L9,2 C9,0.895 8.105,0 7,0 L7,0 Z"
        />
        </Icon>
        <SidebarMenuItemLabel>Dashboard</SidebarMenuItemLabel>
</SidebarMenuItem>
icon9.png

The icon is now visible in our sidebar, but it's positioned incorrectly. Add "display: flex" , "align-items: center", and "padding-left: 30px;" to SidebarMenuItem.

const SidebarMenuItem = styled.li`
        display: flex;
        height: 40px;
        width: 100%;
        align-items: center;
        padding-left: 30px;
        &:hover {
        background: rgba(255, 255, 255, 0.05);
        box-shadow: inset 3px 0 0 0 #ffffff;
        cursor: pointer;
        }
`;

Getting close now, but there still needs to be some space between the icon and the label. Our mockup calls for 20px, specifically.

It would be preferable to space these items with flexbox, but since our tablet mockup calls for the labels to be hidden, we'll stay with the hardcoded values for now.

Add 20px of margin-left to SidebarMenuItemLabel - we don't want to add any margins or paddings to the icon since we'll need a different spacing between the icon in the company logo (designtech) up top.

Untitled picture10.png

Looking great so far. I'm going to wave the magic design wand and implement the rest of the icons.

The Logo & Sign Out Elements

The last items we need to add are the logo up top and the Sign Out link at the bottom.

If you squint really closely, you can see there is a thin line underneath the logo and above Sign Out. In design, we think of these as a "divider" - which they are, technically. But the way we would express this here in CSS would be as border attributes of each of these two elements.

Still in sidebar.js, add two more styled divs: MenuLogo and MenuSignOut.

const MenuLogo = styled.div`
        display: flex;
        align-items: center;
        justify-content: start;
        gap: 16px;
        font-size: 18px;
        line-height: 1.5;
        font-weight: 600;
        height: 45px;
        color: #fff;
        margin: 0px 30px 30px 30px;
        padding-bottom: 20px;
        border-bottom: 1px solid #2e2e33;
`;
 
const MenuSignOut = styled.div`
        font-size: 14px;
        line-height: 1.5;
        font-weight: 500;
        height: 45px;
        color: #fff;
        margin: 200px 30px 60px 30px;
        padding: 20px 0px 0px 30px;
        border-top: 1px solid #2e2e33;
`;

These style properties match the design spec as close as possible, and we just need to add an Icon to our logo, which we'll do in a manner just like we did with the SidebarMenuItems. We'll reference <Icon> in our render function, and copy paste the SVG from Sketch.

At this point, your sidebar.js file should look like this:

import React, { Component } from "react";
import styled from "styled-components";
 
const SidebarContainer = styled.div`
        height: 100vh;
        width: 270px;
        background-color: #252529;
        color: #fff;
        display: flex;
        flex-direction: column;
        font-family: "Roboto", sans-serif;
`;
 
const SidebarMenu = styled.ul`
        display: flex;
        align-items: left;
        flex-direction: column;
        list-style: none;
        width: 100%;
        padding-left: 0px;
`;
 
const MenuLogo = styled.div`
        display: flex;
        align-items: center;
        justify-content: start;
        gap: 16px;
        font-size: 18px;
        line-height: 1.5;
        font-weight: 600;
        height: 45px;
        color: #fff;
        margin: 0px 30px 30px 30px;
        padding-bottom: 20px;
        border-bottom: 1px solid #2e2e33;
`;
 
const SidebarMenuItem = styled.li`
        display: flex;
        height: 40px;
        width: 100%;
        align-items: center;
        padding-left: 30px;
        &:hover {
        background: rgba(255, 255, 255, 0.05);
        box-shadow: inset 3px 0 0 0 #ffffff;
        cursor: pointer;
}
`;
 
const Icon = styled.svg`
        width: 20px;
        height: 20px;
`;
 
const SidebarMenuItemLabel = styled.p`
        font-family: "Open Sans", sans-serif;
        color: #fff;
        font-size: 14px;
        font-weight: 600;
        line-height: 1.3;
        text-align: left;
        padding: 12px 0px;
        margin-left: 20px;
        color: #ffffff;
`;
 
const MenuSignOut = styled.div`
        border-top: 1px solid #2e2e33;
        font-size: 14px;
        line-height: 1.5;
        font-weight: 500;
        height: 45px;
        color: #fff;
        margin: 200px 30px 60px 30px;
        padding: 20px 0px 0px 30px;
`;
class Sidebar extends Component {
render() {
        return (
           <SidebarContainer>
               <SidebarMenu>
                   <MenuLogo>
                   {" "}
                   <Icon viewBox="0 0 20 20">
                   <path
                   width="20px"
                   height="20px"
                   viewBox="0 0 20 17"
                   fill="white"
                   d="M4.23832506,4.349694 L5.39932506,7.16397019 L2.73332506,7.16397019 L4.23832506,4.349694 Z M13.5843251,9.22420166 L16.5773251,9.22420166 L11.0983251,15.2483185 L13.5843251,9.22420166 Z M8.74132506,15.2627401 L3.10032506,9.22420166 L6.24932506,9.22420166 L8.74132506,15.2627401 Z M7.56832506,7.16397019 L6.29332506,4.07362298 L13.5403251,4.07362298 L12.2653251,7.16397019 L7.56832506,7.16397019 Z M11.4153251,9.22420166 L9.91732506,12.8553596 L8.41832506,9.22420166 L11.4153251,9.22420166 Z M17.2603251,7.16397019 L14.4343251,7.16397019 L15.6113251,4.3105496 L17.2603251,7.16397019 Z M19.7873251,7.43592074 L16.9333251,2.26783009 C16.8173251,2.08652972 16.6613251,2 16.4823251,2 C16.2933251,2 16.0773251,2.0133915 15.8533251,2.0133915 L4.18832506,2.0133915 C3.96732506,2.0133915 3.75432506,2.00515058 3.56732506,2.00515058 C3.37732506,2.00515058 3.21332506,2.07313822 3.09432506,2.26783009 L0.198325064,7.75216627 C-0.107674936,8.25383264 -0.0556749356,8.8337878 0.327325064,9.27570745 L9.02132506,18.55499 C9.27732506,18.8516633 9.63032506,19 9.98332506,19 C10.3423251,19 10.7013251,18.848573 10.9593251,18.5436587 L19.6813251,9.01508817 C20.0633251,8.56389747 20.1063251,7.93449676 19.7873251,7.43592074 L19.7873251,7.43592074 Z"
                   id="path-1"
                   />
                   </Icon>
                   designtech
                   </MenuLogo>
               <SidebarMenuItem>
                   <Icon viewBox="0 0 20 20">
                   <path
                   width="20px"
                   height="20px"
                   fill="white"
                   d="M18,17 C18,17.552 17.552,18 17,18 L14,18 C13.448,18 13,17.552 13,17 L13,14 C13,13.448 13.448,13 14,13 L17,13 C17.552,13 18,13.448 18,14 L18,17 Z M18,11 L13,11 C11.895,11 11,11.895 11,13 L11,18 C11,19.105 11.895,20 13,20 L18,20 C19.105,20 20,19.105 20,18 L20,13 C20,11.895 19.105,11 18,11 L18,11 Z M18,6 C18,6.552 17.552,7 17,7 L14,7 C13.448,7 13,6.552 13,6 L13,3 C13,2.448 13.448,2 14,2 L17,2 C17.552,2 18,2.448 18,3 L18,6 Z M18,0 L13,0 C11.895,0 11,0.895 11,2 L11,7 C11,8.105 11.895,9 13,9 L18,9 C19.105,9 20,8.105 20,7 L20,2 C20,0.895 19.105,0 18,0 L18,0 Z M7,17 C7,17.552 6.552,18 6,18 L3,18 C2.448,18 2,17.552 2,17 L2,3 C2,2.448 2.448,2 3,2 L6,2 C6.552,2 7,2.448 7,3 L7,17 Z M7,0 L2,0 C0.895,0 0,0.895 0,2 L0,18 C0,19.105 0.895,20 2,20 L7,20 C8.105,20 9,19.105 9,18 L9,2 C9,0.895 8.105,0 7,0 L7,0 Z"
                   />
                   </Icon>
                   <SidebarMenuItemLabel>Dashboard</SidebarMenuItemLabel>
               </SidebarMenuItem>
               <SidebarMenuItem>
                   <Icon viewBox="0 0 20 20">
                   <path
                   width="20px"
                   height="20px"
                   fill="white"
                   d="M11,5.007 L11,12.007 C11,12.559 10.552,13.007 10,13.007 C9.448,13.007 9,12.559 9,12.007 L9,5.007 C9,4.455 9.448,4.007 10,4.007 C10.552,4.007 11,4.455 11,5.007 L11,5.007 Z M11,15.007 C11,15.559 10.552,16.007 10,16.007 C9.448,16.007 9,15.559 9,15.007 C9,14.455 9.448,14.007 10,14.007 C10.552,14.007 11,14.455 11,15.007 L11,15.007 Z M18,17 C18,17.552 17.552,18 17,18 L3,18 C2.448,18 2,17.552 2,17 L2,3 C2,2.448 2.448,2 3,2 L17,2 C17.552,2 18,2.448 18,3 L18,17 Z M18,0 L2,0 C0.895,0 0,0.899 0,2.003 L0,2.007 L0,18.007 C0,19.112 0.895,20 2,20 L18,20 C19.105,20 20,19.108 20,18.003 L20,2.007 C20,0.902 19,0 18,0 L18,0 Z"
                   id="path-1"
                   />
                   </Icon>
                   <SidebarMenuItemLabel>Service Alerts</SidebarMenuItemLabel>
               </SidebarMenuItem>
               <SidebarMenuItem>
                   <Icon viewBox="0 0 20 20">
                   <path
                   width="20px"
                   height="20px"
                   fill="white"
                   d="M18,17 L18,7 C18,6.448 17.552,6 17,6 L3,6 C2.448,6 2,6.448 2,7 L2,17 C2,17.552 2.448,18 3,18 L17,18 C17.552,18 18,17.552 18,17 L18,17 Z M8,3 L8,4 L12,4 L12,3 C12,2.448 11.552,2 11,2 L9,2 C8.448,2 8,2.448 8,3 L8,3 Z M18,4 C19.105,4 20,4.895 20,6 L20,18 C20,19.105 19.105,20 18,20 L2,20 C0.895,20 0,19.105 0,18 L0,6 C0,4.895 0.895,4 2,4 L6,4 L6,2 C6,0.895 6.895,0 8,0 L12,0 C13.105,0 14,0.895 14,2 L14,4 L18,4 Z M9,15 L9,13 L7,13 C6.448,13 6,12.552 6,12 C6,11.448 6.448,11 7,11 L9,11 L9,9 C9,8.448 9.448,8 10,8 C10.552,8 11,8.448 11,9 L11,11 L13,11 C13.552,11 14,11.448 14,12 C14,12.552 13.552,13 13,13 L11,13 L11,15 C11,15.552 10.552,16 10,16 C9.448,16 9,15.552 9,15 L9,15 Z"
                   id="path-1"
                   />
                   </Icon>
                   <SidebarMenuItemLabel>Customer Tickets</SidebarMenuItemLabel>
               </SidebarMenuItem>
               <SidebarMenuItem>
                   <Icon viewBox="0 0 20 20">
                   <path
                   width="20px"
                   height="20px"
                   fill="white"
                   d="M18,8 L2,8 L2,3 C2,2.448 2.337,2 2.889,2 L6.244,2 C6.626,2 6.974,2.217 7.142,2.56 L8.278,4.879 C8.614,5.565 9.31,6 10.074,6 L16.889,6 C17.441,6 18,6.448 18,7 L18,8 Z M18,17 C18,17.552 17.441,18 16.889,18 L2.889,18 C2.337,18 2,17.552 2,17 L2,10 L18,10 L18,17 Z M17.889,4 L11.125,4 C10.367,4 9.675,3.572 9.336,2.894 L8.442,1.106 C8.103,0.428 7.41,0 6.653,0 L1.889,0 C0.784,0 0,0.895 0,2 L0,18 C0,19.105 0.784,20 1.889,20 L17.889,20 C18.993,20 20,19.105 20,18 L20,6 C20,4.895 18.993,4 17.889,4 L17.889,4 Z"
                   id="path-1"
                   />
                  </Icon>
                  <SidebarMenuItemLabel>Archive</SidebarMenuItemLabel>
               </SidebarMenuItem>
               <SidebarMenuItem>
                  <Icon viewBox="0 0 20 19">
                  <path
                  width="20px"
                  height="19px"
                  fill="white"
                  d="M18,13.4070943 C18,13.8456529 17.729,14.2359392 17.324,14.3787018 L11,16.605388 L11,4.71500707 L11,4.53321581 L16.676,2.53453901 C17.325,2.30550257 18,2.80054996 18,3.50614654 L18,13.4070943 Z M9,4.71500707 L9,16.605388 L2.676,14.3787018 C2.271,14.2359392 2,13.8456529 2,13.4070943 L2,3.50614654 C2,2.80054996 2.675,2.30550257 3.324,2.53453901 L9,4.53321581 L9,4.71500707 Z M17.337,0.117845024 L10.331,2.64443543 C10.117,2.72146562 9.883,2.72146562 9.669,2.64443543 L2.663,0.117845024 C1.362,-0.351525631 0,0.639596213 0,2.05592474 L0,14.1157721 C0,14.9969975 0.547,15.7806514 1.36,16.0620684 L9.68,18.9460789 C9.888,19.0179737 10.112,19.0179737 10.32,18.9460789 L18.64,16.0620684 C19.453,15.7806514 20,14.9969975 20,14.1157721 L20,2.05592474 C20,0.639596213 18.638,-0.351525631 17.337,0.117845024 L17.337,0.117845024 Z"
                  id="path-1"
                  />
                  </Icon>
                  <SidebarMenuItemLabel>Library</SidebarMenuItemLabel>
               </SidebarMenuItem>
               <SidebarMenuItem>
                  <Icon viewBox="0 0 20 20">
                  <path
                  width="20px"
                  height="20px"
                  fill="white"
                  d="M18,11 L14,11 L14,7 L17,7 C17.552,7 18,7.448 18,8 L18,11 Z M18,15 L17.221,15 C16.672,14.39 15.885,14 15,14 C14.647,14 14.314,14.072 14,14.184 L14,13 L18,13 L18,15 Z M15,18 C14.449,18 14,17.551 14,17 C14,16.449 14.449,16 15,16 C15.551,16 16,16.449 16,17 C16,17.551 15.551,18 15,18 L15,18 Z M12,5 L12,15 L9.484,15 C9.038,13.278 7.487,12 5.625,12 C4.538,12 3.556,12.436 2.838,13.142 C2.526,13.448 2,13.216 2,12.779 L2,3 C2,2.448 2.448,2 3,2 L11,2 C11.552,2 12,2.448 12,3 L12,5 Z M7.347,17 C7,17.595 6.362,18 5.625,18 C4.888,18 4.25,17.595 3.903,17 C3.731,16.705 3.625,16.366 3.625,16 C3.625,15.634 3.731,15.295 3.903,15 C4.25,14.405 4.888,14 5.625,14 C6.362,14 7,14.405 7.347,15 C7.519,15.295 7.625,15.634 7.625,16 C7.625,16.366 7.519,16.705 7.347,17 L7.347,17 Z M18,5 L14,5 L14,2 C14,0.895 13.105,0 12,0 L2,0 C0.895,0 0,0.895 0,2 L0,15.234 C0,16.209 0.791,17 1.766,17 C2.213,18.722 3.763,20 5.625,20 C7.487,20 9.037,18.722 9.484,17 L12,17 C12,18.657 13.343,20 15,20 C16.657,20 18,18.657 18,17 C19.105,17 20,16.105 20,15 L20,7 C20,5.895 19.105,5 18,5 L18,5 Z"
                  id="path-1"
                  />
                 </Icon>
                 <SidebarMenuItemLabel>Deliveries</SidebarMenuItemLabel>
               </SidebarMenuItem>
          <MenuSignOut>Sign Out</MenuSignOut>
     </SidebarMenu>
</SidebarContainer>
);
}
}
export default Sidebar;

And at http://localhost:3000, you should see the following:

finished sidebar11.png

Looking great! You created a sidebar component, imported it into your main app.js file, and styled it with styled-components. Very good job.

We'll need to implement some responsive behavior in this sidebar, but first I'll show you how to build the rest of the layout out using CSS Grid.

Many people would have you believe that designing mobile-first is the way to go, but the reality is, this type of application is going to be desktop-first. The goal is to simulate an enterprise dashboard, similar to one of the many enterprise dashboards projekt202 has built for our clients. These dashboards are primarily rendered on desktop browsers by enterprise employees and executives at their desks.

On top of that, one of the main intentions of this tutorial is to show how to compose layouts with CSS Grid, and I believe that is best illustrated here with the desktop layout.

Grid Areas: The Header & Dashboard Modules

all 3 layouts12.jpg

Right, so we have our deliverable here (at a high level) with a mobile, tablet and desktop layout. This will be good for us to get started on planning how to architect this layout with CSS Grid.

We got into how to size elements with CSS Grid in part one of this tutorial, so now that we have a layout in front of us, I'd like to talk about Grid Template Areas.

Grid Template Areas are a feature of CSS Grid layout that let you express your layout semantically in ASCII art-type format. Looking at our desktop layout, it's pretty easy to define areas by looking at the main three parent-level elements of this layout: sidebar, header, container. Magically, we can build this layout with CSS Grid by simply dictating to the browser where each element goes using only their names.

13.jpg

Back in part one of this tutorial, I talked about the fractional unit, rows, and columns in CSS Grid. Now, we'll apply them.

Start by opening App.css in your Create-React-App project. This file is set by default by CRA as your stylesheet for App.js, which is currently the "homepage" of your app. Your App.js instance should look like this, which includes the Sidebar from earlier now imported:

import React, { Component } from "react";
import Sidebar from "./sidebar";
import "./App.css";
class App extends Component {
render() {
     return (
        <div className="App">
            <Sidebar class="sidebar" />
        </div>
        );
     } 
}
export default App;

In your render function, create a div called "grid-container" and wrap Sidebar element inside it.

Append the class "grid-sidebar" to your <Sidebar> element, and while you're at it, add a self-closing div with the class "header" and one with the class "content," both under the sidebar. Like so:

class App extends Component {
render() {
     return (
        <div className="App">
          <div class="grid-container">
            <Sidebar class="grid-sidebar" />
            <div class="header" />
            <div class="content" />
          </div>
        </div>
        );
     }
 }

With the block level elements set now, let's set up our grid container's CSS, which is currently serving as the parent of its three children - sidebar, header, and content.

.container {
   display: grid;
 
   grid-template-columns: 20% 1fr;
   grid-template-rows: 72px 1fr;
 
   grid-template-areas:
 
   "sidebar head head head"
   "sidebar main main main"
   "sidebar main main main"
   "sidebar main main main";
 
   /* grid-container height = 100% of viewport height */
   height: 100vh;
}

Let's break the above block down using the knowledge we gained from part one of this tutorial.

display: grid;

First, we're telling the browser that "grid-container", which we can think of in design terms as "the artboard" in this case, uses CSS Grid layout.

grid-template-columns: 20% 1fr;
grid-template-rows: 72px 1fr;

Second, we're saying - "I want to use a two-column layout - the first of which is 20% of the grid-container (the parent) width, the second of which simply takes up whatever is leftover (one fractional unit). For rows, give me one 72px tall row, and another row with the that takes up the rest of the container's height."

grid-template-areas:
"sidebar head head head"
"sidebar main main main"
"sidebar main main main"
"sidebar main main main"

So here's that ASCII I spoke about earlier. With this block, we're using grid area names to construct our layout. It really is almost like magic.

But one quick problem: We haven't assigned those grid area names to any elements in our code yet. Let's do so now.

Open sidebar.js, and delete the width: line in your SidebarContainer style. If you followed the first section of this post pretty closely, it should be around line 6 of your sidebar.js file. Since we'll be using the sidebar as a grid column, we don’t need to specify the width anymore. Grid will handle that for us.

Time for some more style work - let's go back to App.css.

Let's create a class for each of our layout areas and assign them the area names that we used in our grid-container block.

.grid-sidebar {
   grid-area: sidebar;
}
.header {
   grid-area: head;
}
.content {
   grid-area: main;
}

Let's give the header class a box-shadow value to simulate a bottom-border, per the mockup:

.header {
   grid-area: head;
   box-shadow: 0 1px 0 0 #eaedf3;
}

Just for testing purposes, let's apply a background-color value of purple to the content class. Since the mockup we're building off of calls for a white background, it will be hard to tell if we're building it correctly at first - since there's no content inside of it.

If all went well, here's what you should see:

template area with purple background14.PNG

As you collapse the browser, you'll see that the widths will adjust accordingly. Isn't that cool? Instead of defining manual widths or a million bootstrap column classes, all we had to do was create a few lines of CSS Grid work to come up with this.

And don't get me wrong, this is just a starting point. There are lots of cool features to CSS Grid - like minmax, for example - that you'd want to take advantage of in production. But this is a great way to start understanding how you can leverage the power of grid template areas to build layouts.

We're not done yet - I want to quickly touch on media queries to show how we can make this responsive.

If you've come this far, congrats! You're almost at the finish line. These are a lot of front end concepts to touch on as a designer and you should be very proud if you've made it up to now.

Using Grid Areas and Media Queries for Responsiveness

mobile15.PNG

If you aren't familiar with Media Queries, take a second to check out the Mozilla developer reference here. They're utilized heavily in responsive CSS, and you can do everything from resize elements at certain breakpoints to showing or hiding elements at certain points. The sky is really the limit, and it is a fairly light learning curve if you're already in this far.

It looks like our mobile layout starts at 375px, per our handoff spec, and gets rid of the sidebar. Begin in App.css and write this media query. Under your block for .grid-container is preferable since this media query will only pertain to grid-container, but it will work anywhere in App.css just the same.

@media screen and (max-width: 375px) {
   .grid-container {
     grid-template-columns: 1fr;
     grid-template-rows: 72px 1fr;
     grid-template-areas:
     "head"
     "main";
   }
}

All this media query does is simply tell the browser to update grid-container's columns, rows and area value. You're not doing anything new, simple changing the CSS you already wrote for grid-container. And only at 375px or narrower.

But in order for us to hide the sidebar, we'll have to go back to our styled-component in sidebar.js.

Add this media query in the SidebarContainer block:

@media (max-width: 375px) {
   display: none;
}

This one will have to be inside the SidebarContainer block, whereas the first media query can be placed anywhere inside App.js.

Now, I added a quick <p>Ops Suite</p> inside the header HTML (App.js)  for testing purposes. Let's see the result:

mediaqueries16.gif

Alright! We're cooking with Media Query brand gasoline now. Once the browser width hits 375px, we hide the sidebar and set the CSS Grid layout to one column and two rows - one for the header, and one for the rest of the browser's height.

Go forth, and make it your own

So this is enough to get you started. To recap, you:

-Set up Node/NPM

-Stood up a create-react-app project locally

-Built a sidebar with styled-components in React

-Used CSS Grid layout template areas to build your layout

-Used media queries to adjust your overarching layout a mobile breakpoint

That's a good chunk to break off! Hopefully, you're starting to see that getting things up and going in the browser is not as difficult as it may have been, say, four years ago.

If you are a designer looking to design or prototype in the browser rather than static mocks, the time has never been better. With tools like create-react-app and CSS Grid layout at your disposal, scaffolding a project takes minutes, rather than days.

I highly recommend continuing to double down on these concepts as you continue your design career path. Again, I'm not saying designers need to code, but knowing the concepts - even if you aren't a master - will empower the work you do and the collaboration you have with developers.


projekt202 is the leader in experience-driven software strategy, design and development. We have a unique and established methodology for understanding people in context — we reveal unmet needs — which drives everything we do. This leads to a crisp, clear understanding of the customer, which shapes the design and development of new solutions and experiences. We have the expertise, teams, skills and scale to deliver sophisticated software solutions that improve any and all touchpoints across the user journey.

projekt202 has spent over 15 years bringing to life and to market compelling experiences through our Experience Strategy & InsightUser ExperienceSoftware DevelopmentMarketing & Analytics, and Program Management practices. Our talented team has delivered emotionally-rich and intuitive solutions for global brands and clients such as 7-Eleven, Capital One, Dell, Mercedes-Benz Financial Services, Neiman Marcus, Samsung Electronics, and The Container Store, among many others.

projekt202 has offices in AustinChicagoDallas, San Francisco and Seattle.

CONTACT US TODAY TO DISCUSS HOW THE PROJEKT202 TEAM CAN DELIVER RESULTS FOR YOU AND YOUR TEAM.  

FOLLOW US ON LINKEDIN AND TWITTER FOR THE LATEST PROJEKT202 NEWS.