Comp to Code from the Content Out

A no-nonsense approach
for building out contemporary web comps
without compromising on quality.

Phase 3: Streamlined CSS

With simple, clean, and well-labeled HTML in place we can now revisit the visual component of our site by adding streamlined CSS. Following the content-out pattern, we'll focus first on content styles, move on to build our overall layouts and micro-layouts, and then fine-tune each area as needed.

A. Responsive Content Styles

Responsive web design changed the game for our CSS approach by placing a helpful emphasis on the scalability of our designs. But before we get into the more complex features of building out our layout, we can first focus on what is common between all our comps: the content itself. Whether we're truly thinking from a "mobile-first" perspective, or establishing a baseline for older browsers we can make a lot of helpful headway with building out or visual style by focusing just on content styles: font, weight, size, color, leading, alignment, decoration, and other related styles. Consider the following approach:

  1. Implement a reset stylesheet such as Eric Meyer's reset. This wipes out most of the default styles on content elements, freeing you to add your own styles to conform those elements to your desired aesthetics as simply as possible.
  2. Set the default font family and a font size of 62.5% on the <body> element. This both establishes an appropriate default font for text on the site as well as resets the base font size to the equivalent of 10pts, thus making it simple to transfer point-based font sizes from your comp. With this in place, a font size of 14 points in the comp can be entered as 1.4 ems in CSS.
  3. Add CSS rules to provide baseline content styles for primary content elements such as paragraphs, list items, headings, blockquotes, table cells and table headings as needed.
  4. Add customizations for any elements needed using nested selectors or the classes you added in your markup earlier. Add new classes if needed in order to customize elements. All along the way here, you can focus first on the styles you want in place for mobile devices. Then, consider how each of these adjusts for larger devices and add media queries as needed that allow things to scale up using the min-width filter.

B. Responsive Layout Using the CSS Grid

You'll be glad to have made so much progress in the previous step, and pleasantly surprised at how quickly your overall layout will come together next with the new CSS Grid Module. So now pull out the sketches of the grids for container and compound units you made in the Phase 1 and begin to build out your overall layout.

  1. Implement the appropriate grid settings each of the outermost container units using the sketches you made earlier.
    1. Set the container unit to display:grid;
    2. Use grid-template-columns to provide the dimension of each vertical grid track.
    3. If necessary, use grid-template-rows to provide the dimension of each horizontal grid track. As mentioned earlier, row height might be best set using the auto keyword. This is the default setting and rows are generated automatically, so this setting can often be omitted.
    4. Use grid-template-areas to establish named grid areas for each layout unit.
  2. Select each layout unit and assign it to its grid area using the grid-area property and the corresponding named grid area you set up on the container.
  3. Add additional box model styles to each layout unit in order to achieve the desired appearance to match your comp such as spacing with padding, backgrounds, and borders.

Note that this can be used for any responsive breakpoint, including your default setup for the mobile size. Simply set up a new grid or reapply grid areas using media queries. This makes shifting the size of elements and even their order of presentation effortless without changing a single character in the markup.

Creating Micro-layouts

With the overall layout in place simply and easily thanks to the CSS Grid Module, we can now circle inward and assess each compound unit where micro-layouts are at work. Remember that some micro-layouts are easily accomplished with a simple float:left or float:right, such as where an image is set to the right edge of a content group allowing text to wrap around it. Other micro-layouts can easily be accomplished with the flexbox, such as a product thumbnail grid. But where a complex grid is needed, use the same overall process as above. Just start with the compound unit set to display:grid and the other necessary settings and select the group's child elements to apply to each desired grid area.

C. Fine-tune for Perfection

Finally, circle back through the layout to finesse settings on elements as needed, now that content styles and layout arrangement can be seen together.

Case Studies

Read the case study introductions »

Corner Bakery

Having completed our analysis in phase 1 and put our markup in place in phase 2 we can now finally apply our design style and layout with streamlined CSS.

First, we lay in fonts and basic content styles. With the help of the Meyer Reset and other techniques outlined in the process above we end up with an attractive basic site that is quite effective for smartphones and other small devices

// Imports
@import url(reset.css);
@import url('https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,700|Teko:500,600');

// Mixins
.body-copy() {
    font-size:1.6em;
    line-height: 2.5/1.6em;
    font-weight:300;
}
.btn(@width) {
    display: block;
    background-color: @color-aqua;
    margin:0 auto;
    box-sizing: border-box;
    padding:15px 25px;
    width:@width;
    .font-open-sans;
    font-size:1.6em;
    font-weight:400;
    text-align: center;
    color:white;
    border:none;
    outline:none;
}
.font-teko() { font-family: "Teko", sans-serif; }
.font-open-sans() { font-family: "Open Sans", sans-serif; }
.thumbs(@font-size:2.3em, @font-weight:700) {
    li {
        text-align:center;
        font-size:1em;
        img { 
            display:block; 
            margin:0 auto;
        }
        b {
            display: block;
            .font-teko;
            font-size:@font-size;
            font-weight:@font-weight;
            color:@color-aqua;
            margin-bottom:8px;
            text-transform: lowercase;
        }
    }
}

// Variables
@color-aqua: #64A4A2;
@color-green: #5C7C4B;
@color-grey: #4A4A4A;
@color-orange: #C2762E;

// Content styles
a {
    color:@color-aqua;
    font-weight:400;
    text-decoration: none;
}
.allergies {
    text-align: center;
    strong { display: block; }
}
body {
    .font-open-sans;
    font-size:62.5%;
    color:@color-grey;
}
.categories { .thumbs(); }
dd {
    .body-copy;
    margin-bottom:12px;
}
dt {
    .body-copy;
    margin-bottom:12px;
}
footer { 
    margin:25px;
    border-top:2px solid @color-green;
    padding:20px 0 0;
    p { text-align:center; }
}
.grid-item {
    font-size:1em; 
    input { .btn(250px); }
}
h1 { text-align:center; }
h2 {
    .font-teko;
    font-size:4.3em;
    font-weight:500;
    text-align: center;
    color:@color-green;
    text-transform: lowercase;
    border-bottom:2px solid @color-green;
    margin:25px 0;
    padding-bottom:13px;
    a { color:@color-green; }
}
header { padding:65px 25px 10px; }
img { max-width:100%; }
input[type="date"],
input[type="email"], 
input[type="tel"],
input[type="text"],
textarea {
    padding:5px;
    font-size:1em;
    .font-open-sans;
    font-weight:300;
    color:@color-grey;
    border:1px solid @color-grey;
}
label, .label {
    margin-top:7px;
    margin-bottom:5px;
    display: block;
}    
li { .body-copy; }
main {
    box-sizing: border-box;
    padding:25px;
}
nav {
    a { 
        color: @color-grey; 
        font-weight:300;
    }
    li { 
        text-align:center; 
        font-size:2.3em;
        border-top:3px solid transparent;
        &.active {
            border-color:@color-orange;
            a { 
                color:@color-orange;
                font-weight:700;
            }
        }
    }
}
.order {
    font-size:1em;
    a { .btn(250px); }
}
p { 
    .body-copy;
    margin-bottom:25px;
}
.products { .thumbs(1.9em, 500); }
strong {
    font-style: italic;
    font-weight:600;
    color:@color-orange;
}

View the project to this point »

Next we move on to setting up the page layouts beginning with the overall persistent elements as follows:

body {
    width:100%;
    @media screen and (min-width:1200px) {
        margin:0 auto;
        width:1200px;
    }
}
nav {
    li { margin:0 33px; }
    ul { 
        display:flex;
        justify-content:center; 
    }
    a { 
        display:block; 
        padding:2px 5px;
    }
}
footer { 
    @media screen and (min-width:600px) {
        width:550px;
        margin:0 auto; 
    }
}

Now we can hone in on the specific pages, starting with the home page where our we planned two CSS grid implementations

.grid-home {
    @media screen and (min-width:760px) {
        display:grid;
        grid-template-columns:1fr 1fr 1fr;
        grid-gap:50px;
        grid-template-areas:
            "banner banner hrs"
            "banner banner about"
            "menu   orders about";
    }
    .about { grid-area: about; }
    .banner { grid-area: banner; }
    .hrs { grid-area: hrs; }
    .menu { grid-area: menu; }
    .orders { grid-area: orders; }
}
.grid-info {
    display:grid;
    grid-template-columns:2fr 3fr;
}

Next we can set up the tile layout in the main menu page very quickly using flexbox settings similar to what we had on the navigation items but also ensure the tiles wrap by setting flex-flow:row wrap. Then each item inside this unit needs a width and a few other simple settings to ensure elements get sized appropriately. We'll start these out at 50% width each and keep this two-up format until the device width is 760 pixels or greater. At that point we'll switch to a three-up layout with the items set to 33% widths. In either situation, we can also add justify-content:center so that a row with fewer than the maximum number of items will center the remaining items.

.categories {
    display:flex;
    justify-content:center; 
    flex-flow: row wrap;
    li {
        width:50%;
        box-sizing: border-box;
        padding:20px;
        @media screen and (min-width:760px) {
            width:33%;
        }
    }
}

We carry much of this same idea over to the sample menu category page. However, a small .intro unit opens the <main>, which we'll simply center at a fixed width of 550 pixels when the device width is 600 pixels or greater.

Implementing a flexbox setup on the categories' product thumbnails similar to the main menu page thumbnails quickly assembles the desired tile layout. However, in this page we'll want two-up tiles until 600 pixels device width, three-up from there to 800 pixels, four-up to 1000 pixels, and five-up for any size greater than that. The final unit on this page containing a link to place an order implements a fixed width and auto margins.

.intro {
    @media screen and (min-width:600px) {
        width:550px;
        margin:0 auto;
    }
}
.products {
    display:flex;
    justify-content:center; 
    flex-flow: row wrap;
    li {
        width:50%;
        box-sizing: border-box;
        padding:20px;
        @media screen and (min-width:600px) {
            width:33%;
        }
        @media screen and (min-width:800px) {
            width:25%;
        }
        @media screen and (min-width:1000px) {
            width:20%;
        }
    }
}

Finally, the order form implements the same .intro unit as on the menu category page. Then the order form itself implements a grid system. But rather than flatten our semantic list structure to place grid settings on the form itself, we'll keep the list and place the grid settings on each list item in the form. We implement the grid responsively as follows:

While a little more complex than the other grids, you can see this still came together quickly and simply thanks to analysis from phase 1, and solid markup from phase 2.

.grid-order-form {
    li, .grid-item {
        display:grid;
        grid-template-columns:1fr 1fr;
        margin-bottom:12px; 
        &.input, &.textarea {
            grid-template-areas:
                "label label"
                "field field";  
            label { grid-area: label; }
            input, textarea { grid-area: field; }
        }
        &.radios {
            grid-template-areas:
                "label label"
                "r1 r2"; 
            .label { grid-area: label; }
            label:nth-of-type(1) { grid-area: r1; }
            label:nth-of-type(2) { grid-area: r2; } 
        }
        input[type="submit"] { margin-left:0; }
    }
    @media screen and (min-width:600px) {
        width:600px;
        margin:0 auto;
        label, .label {
            margin-top:7px;
            margin-right:50px;
            text-align:right;
        }
        li, .grid-item {
            margin-bottom:25px;
            grid-template-columns:4fr 3fr 3fr 2fr;
            &.input {
                grid-template-areas:
                    "label field field .";
            }
            &.radios {
                grid-template-areas:
                    "label r1 r2 .";
            }
            &.textarea {
                grid-template-areas:
                    "label field field field";
            }
        }
    }   
}

With all the layouts in place we can sweep back through each page to tidy up spacing and element dimensions as needed and test the responsive layouts, making adjustments as needed.

View the project to this point »

Act On It

Adding style to our single-page application begins with just content styles. We implement the Meyer reset to ensure default styles are eliminated and then add fonts, colors, backgrounds, and basic spacing. We also can create a number of variables and mixins using LESS or a similar technology to reduce repetition. In our case, we add mixins to set fonts, add shadows in Material Design fashion, add and remove button styling, style form fields, and implement the "image replacement" technique for where we want to use icons in place of plain text. Finally, we set up some styles to govern appearance of elements that toggle on and off such as hiding accessible text, and expanding/contracting the options bar. We end up with a functional application that just lacks our full layout.

@import url("meyer-reset.css");
@import url('https://fonts.googleapis.com/css?family=Roboto+Condensed:400,700|Roboto:300,400,500');

// Variables

@color-dkgrey: #424242;
@color-mdgrey: #616161;
@color-grey: #757575;
@color-norm: #00e5ff;
@color-urg: #ffc400;
@color-back: #e0e0e0;
@color-red: #ff5722;
@color-ltgrey: #fefefe;
@color-green: #00e676;

@size-desktop: 990px;
@size-almost-desktop: 680px;

// Utility Mixins

.btn(@height:40px, @bg-color:@color-green, @color:@color-dkgrey, @border-color:transparent, @font-size:1em) {
  border-radius:@height/2;
  height:@height;
  background-color:@bg-color;
  border:1px solid @border-color;
  color:@color;
  padding:0 @height/2;
  text-transform: uppercase;
  font-weight:300;
  .md-shadow(1);
  &:hover { .md-shadow(4); }
}
.btn-off(@color:white) {
  border:none;
  height:auto;
  background-color:transparent;
  color:@color;
  padding:0;
  box-shadow:none;
  &:hover { box-shadow:none; }
}
.font-roboto-cond() {
  font-family: 'Roboto Condensed', sans-serif;
}
.font-roboto() {
  font-family: 'Roboto', sans-serif;
}
.img-replacement(@width:auto) {
  text-indent: -9999px;
  background-repeat:no-repeat;
  background-position:center center;
  width:@width;
}
.input-styles() {
  background-color:transparent;
  font-size:1em;
  border:none;
  border-bottom:1px solid @color-dkgrey;
  padding:10px;
  font-weight:300;
  display: block;
  width:100%;
  box-sizing:border-box;
}
.md-shadow(@depth:2) {
  box-shadow: 0 (@depth * 3 * 1px) (@depth * 3 * 2px) rgba(0,0,0,(0.12 + (4*(@depth - 1))/100)),
  0 1px 2px rgba(0,0,0,0.24);
  transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}

// Master Styles

a { color: @color-dkgrey; }
.accessible {
  display: block;
  height:0;
  overflow:hidden;
}
.add-category {
  margin:10px;
  display: block;
}
.add-task { display: none; }
.app-options {
  background-color: @color-grey;
  padding:5px 10px;
  position: relative;
  &.expanded {
    .btn-expand {
      background-image: url("../images/icon-up-lt.png");
    }
    .secondary { display: block; }
  }
  .account {
    text-align:right;
    img {
      width:100px;
      height:100px;
    }
  }
  button {
    .btn(40px, @color-grey, white, white);
    &.on {
      background-color: white;
      color:@color-grey;
    }
  }
  .btn-expand {
    .btn-off;
    .img-replacement(60px);
    background-image:url("../images/icon-down-lt.png");
  }
  .grouping {
    p {
      margin:10px 0;
      @media screen and (min-width:@size-desktop) { margin-top:30px; }
    }
  }
  .option {
    .label {
      color:white;
      text-align:left;
      margin-bottom:5px;
    }
  }
  .options {
    display: flex;
    margin:0 auto;
  }
  .secondary { display: none; }
  .sort-options {
    button {
      .img-replacement(60px);
      &.sort-asc {
        background-image: url("../images/icon-up-lt.png");
        &.on { background-image: url("../images/icon-up-dk.png"); }
      }
      &.sort-desc {
        background-image: url("../images/icon-down-lt.png");
        &.on { background-image: url("../images/icon-down-dk.png"); }
      }
    }
  }
  @media screen and (min-width:@size-almost-desktop) {
    .secondary { display: block; }
    .btn-expand { display: none; }
    .account {
      img {
        margin-top:5px;
        width:60px;
        height:60px;
      }
    }
  }
}
b { font-weight:500; }
body {
  width: 100%;
  height: 100%;
  font-size:62.5%;
  .font-roboto;
  color:@color-dkgrey;
  font-weight:300;
}
.btn-group {
  & > li button {
    border-radius:0;
    padding-left:10px;
    padding-right:10px;
  }
  & > li:first-child button {
    padding-left:20px;
    border-top-left-radius: 20px;
    border-bottom-left-radius: 20px;
  }
  & > li:last-child button {
    padding-right:20px;
    border-top-right-radius: 20px;
    border-bottom-right-radius: 20px;
  }
}
button {
  .btn(40px);
}
.category {
  .label {
    font-size:2.1/1.4em;
    font-weight:400;
    padding:5px 0 5px 10px;
    margin:15px 0 0;
    .font-roboto-cond;
    &:hover {
      background-color:@color-mdgrey;
    }
  }
  .options {
    display: none;
  }
}
.filter-types {
  button {
    .img-replacement(30px);
    height:30px;
    padding:0;
    border:none;
    opacity:0.3;
    &.on {
      opacity:1;
    }
  }
  .options {
    margin-top:10px;
    li {
      margin-right:10px;
      &:last-child {
        margin-right:0;
      }
    }
  }
  .task-type-norm,
  .task-type-norm.on {
    background-color: @color-norm;
  }
  .task-type-urg,
  .task-type-urg.on {
    background-color: @color-urg;
  }
  .task-type-back,
  .task-type-back.on {
    background-color: @color-back;
  }
}
h1 {
  padding:10px 0 0;
  font-size:2.1em;
  .font-roboto-cond;
  color:@color-norm;
  font-weight:400;
  @media screen and (min-width:@size-desktop) {
    color:@color-dkgrey;
    font-size:3.7em;
    padding-top:20px;
    margin:10px;
    &:before {
      display: block;
      content:"";
      width:20px;
      height:20px;
      background-color: white;
      float: left;
      margin-right:10px;
      position: relative;
      top:-20px;
    }
  }
}
h2, .h2 {
  font-size:2.1em;
  font-weight:500;
}
html { height:100%; }
header {
  background-color: @color-dkgrey;
  @media screen and (min-width:@size-desktop) { background-color:@color-norm; }
}
.img-profile { border-radius:50%; }
li, p {
  font-size:1.4em;
  line-height:2/1.4em;
  font-weight:300;
  li, p {
    font-size:1em;
  }
}
main { overflow:auto; }
.modal {
  display: none;
  &.on {
    background-color: fadeout(black, 30%);
    display:block;
  }
  .modal-content {
    background-color: white;
    .md-shadow(3);
  }
}
nav {
  background-color:@color-dkgrey;
  color:white;
  box-sizing:border-box;
  &:hover {
    .category-list { display: block; }
  }
  a {
    color:white;
    text-decoration: none;
  }
  .category-list {
    display: none;
    position: absolute;
    left:0;
    top:40px;
    background-color: @color-dkgrey;
    width:100%;
    padding:10px 0;
  }
  .mobile-nav-link {
    display: block;
    .img-replacement(60px);
    background-image:url("../images/icon-mobile-nav.png");
    height:40px;
  }
  @media screen and (min-width:@size-desktop) {
    padding:10px;
    .mobile-nav-link { display: none; }
    .category-list {
      padding-top:0;
      background-color:transparent;
      width:auto;
      position: static;
      display: block;
    }
  }
}
.project {
  padding:3px 10px 3px 30px;
  margin-bottom:1px;
  &:hover { background-color:@color-mdgrey; }
}

.task {
  .md-shadow(2);
  width:300px;
  margin:0 10px 10px 0;
  padding:10px;
  &:hover { .md-shadow(4); }
}
.task-type-norm { background-color: @color-norm; }
.task-type-urg { background-color: @color-urg; }
.task-type-back { background-color: @color-back; }
.task-editor {
  width:100%;
  .basic-options {
    height:290px;
    label { display: none; }
  }
  .content textarea {
    height:120px;
    .input-styles;
    margin-top:10px;
  }
  .date-due input { .input-styles; }
  .other-options {
    padding:10px;
    background-color:@color-grey;
    height:80px;
    box-sizing:border-box;
    .delete-task {
      margin-top:10px;
      .img-replacement(40px);
      background-image: url("../images/icon-close-dk.png");
      background-color:@color-red;
    }
    .save-task { margin-top:10px; }
  }
  .types {
    .label {
      text-align: center;
      color:white;
      margin-bottom:5px;
    }
    .options {
      display:flex;
      li {
        text-indent:-9999px;
        .md-shadow();
        padding:0;
        width:30px;
        height:30px;
        border-radius:15px;
        margin-right:10px;
        opacity:0.5;
        &.on { opacity:1; }
        &:hover { .md-shadow(4); }
        &:last-child { margin-right:0; }
        input { display: none; }
      }
    }
  }
  @media screen and (min-width:600px) { width:600px; }
}
.task-group-heading { margin:20px 10px 5px; }
.task-list { padding:10px; }

View the project to this point »

Next we can begin to implement the various grid or flexbox layouts throughout our application. Starting with the outermost grid, we planned one setup for mobile and one for desktop in order to display our four primary components. Following a mobile-first mindset, we implement the mobile settings based on our sketches. Then we add the desktop settings inside a media query for our minimum desktop size.

main {
    ...
    display: grid;
    grid-template-columns: [left] 60px 1fr [right];
    grid-template-rows: [top] 40px 1fr auto [bottom];
    grid-template-areas:
        "nav     brand"
        "tasks   tasks"
        "options options";

    @media screen and (min-width:@size-desktop) {
    grid-template-columns: [left] 300px 1fr [right];
    grid-template-rows: [top] 80px 1fr [bottom];
    grid-template-areas:
        "brand options"
        "nav   tasks";
    }

    header { grid-area: brand; }
    nav { grid-area: nav; }
    aside { grid-area: options; }
    main { grid-area: tasks; }
}

Note that we made use of named grid lines for the outermost lines of the grid. This is so that we can instruct the modal window frame to fill the whole grid when its on. We can then select .modal and use grid-row and grid-column to specify these lines as start and end points. Finally, we need to set z-index to ensure this element stacks above the others when it is on.

.modal {
    display:none;
    &.on {
        background-color: fadeout(black, 30%);
        display:block;
        grid-column:left/right;
        grid-row:top/bottom;
        z-index:10;
    }
    ...
}

Moving on to the detail areas of the interface, the main task area can be set up by applying some simple flexbox settings to each of the .task-list elements. We turn on display:flex and then set flex-flow: row wrap. This way all our tasks will tile up into neat rows. As our device size changes we'll see a varying number of tasks per row.

.task-list {
    padding:10px;
    display:flex;
    flex-flow: row wrap;
}

Moving in further still, the individual tasks are also easy to set up thanks to the grid. Our two-column, to-row microlayout from before stays the same across all device sizes, so one simple grid is all we need.

.task {
    ...
    display:grid;
    grid-template-columns: 30px 1fr;
    grid-template-rows:30px 1fr;
    grid-template-areas:
        "status date"
        ".      content";
    .status { grid-area: status; }
    .due-date { grid-area: date; }
    .content { grid-area: content; }
}

When we click on a task the task editor modal window appears. Earlier we set up the modal frame to span the full application grid but we can now focus on getting the content panel to display as desire. Earlier we determined that this microlayout could be set up using a nine-cell grid with some fixed settings at the mobile size but more flexibility at the larger desktop size. Once again the grid makes short work of this.

.modal {
    ...
    &.on {
        ...
        display:grid;
        grid-template-columns: 10px 1fr 10px;
        grid-template-rows:1fr auto 1fr;
        grid-template-areas:
            ". .       ."
            ". content ."
            ". .       .";
        @media screen and (min-width:600px) {
            grid-template-columns:1fr auto 1fr;
            grid-template-rows:1fr auto 1fr;
        }
        .modal-content {
            grid-area:content;
        }
    }
}

The task editor should also implement the same grid settings as the basic task on the .other-options element that contains the three editable fields. This results in the upper portion of the modal displayed as desired. The lower portion is also easy to set up based on my analysis from earlier. A simple three-column grid allows us to alter the display order.

.task-editor {
    ...
    .other-options {
        ...
        display:grid;
        justify-content:space-between;
        grid-template-columns:auto auto auto;
        grid-template-areas: "delete types save";

        .delete-task { grid-area: delete; }
        .save-task { grid-area: save; }
        .types { grid-area: types; }
    }
    ...
}

One small addition here is setting the justify-content:space-between setting. This ensures equal spacing between each column while keeping the leftmost and rightmost items hugging the outer edges.

Last of all we lay out the option bar. Our analysis from earlier will help to set up these layouts. With this complex analysis complete, and following our models from other similar grids, this is pretty simple to set up.

.grid-option-bar() {
  display:grid;
  grid-spacing: 10px;
  justify-content:space-between;
  grid-template-columns: auto auto auto auto;
  grid-template-areas:
    "sort     types    types   expand"
    "status   status   account account"
    "grouping grouping account account";

  @media screen and (min-width:@size-desktop) {
    grid-template-columns: auto auto auto auto auto;
    grid-template-areas:
      "sort types status grouping account";
  }

  .sort-options { grid-area: sort; }
  .filter-types { grid-area: types; }
  .filter-status { grid-area: status; }
  .grouping { grid-area: grouping; }
  .account { grid-area: account; }
  .btn-expand { grid-area: expand; }
}

View the project to this point »

However as we move on to investigate the larger application and fine-tune elements we can see this area could use more work. The transition from mobile to desktop could be smoother with the compact layout being a bit unnecessary when the device size is larger than 680px. The grid system in that area can also be simplified once the device is larger than 500px. At this size the grouping checkbox could fit on the same row as the status filter, especially if the account image snapped to the smaller size it will have in the desktop arrangement. So we'll add two new media queries to the grid settings for the .app-options to smooth this out a bit.

.grid-option-bar() {
    display:grid;
    ...
    @media screen and (min-width:500px) {
        grid-template-columns: auto auto auto;
        grid-template-areas:
            "sort     types    expand"
            "status   grouping account";
    }
    @media screen and (min-width:@size-almost-desktop) {
        grid-template-columns: auto auto auto auto auto;
        grid-template-areas:
            "account sort types status grouping";
    }
    ...
}

We'll also alter the media queries for other elements in the .app-options as a result.

.app-options {
    ...
    .account {
        img {
            ...
            @media screen and (min-width:500px) {
                margin-top:5px;
                width:60px;
                height:60px; 
            }
        }
    .grouping {
        p {
          margin:10px 0;
          @media screen and (min-width:@size-almost-desktop) { margin-top:30px; }
        }
    }
    ...
    @media screen and (min-width:@size-almost-desktop) {
        ...
    }
}

Finally we must consider how to style the add task button that floats in the lower-right of our application throughout the various layouts. Overall it can be absolutely positioned in the bottom right. In the mobile arrangement it should sit just on the top portion of the option bar, but in the desktop arrangement it sits closer to the bottom of the window. However, when the mobile option bar is expanded this button should slide up with the taller option bar.

.add-task {
    position: absolute;
    z-index:2;
    bottom:60px;
    right:15px;
    .img-replacement(60px);
    background-image: url("../images/icon-add-lg-dk.png");
    height:60px;
    border-radius:30px;
    box-sizing:border-box;
    &.expanded {
        bottom:165px;
    }
    @media screen and (min-width:@size-almost-desktop) {
        &.expanded { bottom: 60px; }
    }
    @media screen and (min-width:@size-desktop) {
        bottom:15px;
        &.expanded { bottom: 15px; }
    }
}

View the project to this point »

Folio

Our CSS can come together rather easily thanks to our analysis from phase 1 and solid markup in place from phase 2. First, we implement content styles and mixins to help keep repeating settings as efficient as possible.

@import url("meyer-reset.css");
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300i,400,400i,600,600i');


@color-grey: #37474F;
@color-blue: #03A9F4;
@color-green: #00C853;
@color-red: #F44336;
@color-orange: #FF9800;
@color-ltgrey: #ECEFF1;

@size-desktop:960px;

.btn() {
  border: 1px solid @color-ltgrey;
  box-sizing: border-box;
  display: block;
  text-align: center;
  color:@color-grey;
  background-color: white;
  padding:11px 30px;
  transition:0.2s all linear;
  text-decoration: none;
  width:100%;
  &:hover {
    background-color:@color-grey;
    color:white;
  }
  &:focus { outline:none; }
  &.ok {
    border-color: @color-green;
    color: @color-green;
    &:hover {
      background-color: @color-green;
      color: white;
    }
  }
  &.edit {
    border-color: @color-orange;
    color: @color-orange;
    &:hover {
      background-color: @color-orange;
      color: white;
    }
  }
  &.cancel {
    border-color: @color-red;
    color: @color-red;
    &:hover {
      background-color: @color-red;
      color: white;
    }
  }
  &.info {
    border-color: @color-blue;
    color: @color-blue;
    &:hover {
      background-color: @color-blue;
      color: white;
    }
  }
}
.btn-round(@bg-color:white, @size:40px) {
  display: block;
  width:@size;
  height:@size;
  border-radius: @size/2;
  background-color:@bg-color;
  border:1px solid white;
  box-sizing: border-box;
  padding:0;
  cursor: pointer;
  &:hover { background-color: @bg-color; }
}
.font-ssp() {
  font-family: 'Source Sans Pro', sans-serif;
}
.img-replace() {
  text-indent:-9999px;
  background-position:center center;
  background-repeat:no-repeat;
}
.input() {
  .font-ssp;
  font-size:14px;
  color:@color-grey;
  border: 1px solid @color-ltgrey;
  padding:6px 8px;
  line-height:20px;
  display: block;
  width:100%;
  box-sizing:border-box;
  outline:none;
  font-weight:300;
  &:focus { border-color:@color-blue; }
}


// Styles

a {
  color:@color-blue;
  text-decoration: none;
}

body {
  .font-ssp;
  font-weight:300;
  color:@color-grey;
}

.breadcrumbs {
  font-weight:400;
  margin-left:20px;
}

.btn-edit-collection {
  width:45%;
  @media screen and (min-width:600px) {
    width:260px;
  }
}

.btn-comments {
  .btn-round(transparent);
  background-repeat:no-repeat;
  background-position:center 6px;
  background-color: transparent;
  background-image:url("../images/icon-bubble-green.png");
  border:none;
  text-align: center;
  color:white;
  overflow: hidden;
  &.comments-unread { background-image:url("../images/icon-bubble-orange.png"); }
  &.comments-ok {
    .img-replace;
    background-image:url("../images/icon-plus-lt.png"), url("../images/icon-bubble-green.png");
    background-position:center 13px, center 6px;
  }
}

.btn-public {
  .btn;
  margin-top:10px;
  @media screen and (min-width: 600px) {
    position: absolute;
    margin:0;
    width:160px;
    top:89px;
    right:20px;
  }
  @media screen and (min-width: @size-desktop) {
    top:42px;
  }
}

.btn-status {
  .img-replace;
  &.status-approved {
    .btn-round(@color-green);
    background-image:url("../images/icon-check.png");
  }
  &.status-unreviewed {
    .btn-round();
    border-color:@color-grey;
  }
  &.status-denied {
    .btn-round(@color-red);
    background-image:url("../images/icon-x-lt.png");
  }
  &.status-needs-attention {
    .btn-round(@color-orange);
    background-image:url("../images/icon-attention.png");
  }
}

button {
  .btn;
  .font-ssp;
  font-size:14px;
  font-weight:400;
}

.category {
  border-bottom:1px solid @color-ltgrey;
  .details {
    padding:0 20px 20px;
    box-sizing: border-box;
  }
  &:last-child { border:none; }
  .items-required { font-weight:600; }
  .btn-reorder {
    .img-replace;
    background-image: url("../images/icon-drag.png");
    background-position:right center;
  }
  .options {
    padding-top:22px;
    box-sizing: border-box;
  }
  @media screen and min-width(@size-desktop) {
    padding:0 20px 20px;
  }
}

.collection-group {
  margin:20px 0 15px;
  .label {
    font-weight:600;
    margin-bottom:5px;
    display: block;
  }
  a {
    display: block;
    padding:5px 10px 5px 20px;
    font-size: 14px;
    line-height: 18px;
    text-decoration: none;
    font-weight:400;
    color:@color-blue;
    &:hover { background-color:@color-ltgrey; }
  }
  @media screen and (min-width:@size-desktop) {
    &.active a {
      background-color: #03A9F4;
      background-image: url("../images/icon-indicator.png");
      background-repeat:no-repeat;
      background-position: 7px center;
      color:white;
      font-weight:400;
    }
  }
}

footer {
  border-top:1px solid @color-ltgrey;
  margin-top:40px;
  padding:20px 0;
  .attribution { text-align: center; }
  .brand-folio {
    display: block;
    text-transform: uppercase;
    font-size: 23px;
    font-weight: 600;
    line-height: 30px;
    margin-bottom:2px;
  }
  .brand-cu {
    .img-replace;
    display: block;
    margin:12px auto;
    background-image: url("../images/logo-cu.png");
    width: 205px;
    height: 42px;
  }
  .copyright {
    padding:20px;
    box-sizing:border-box;
  }
}

form {
  label {
    font-weight:400;
    margin-bottom:5px;
    display: block;
  }
  li { margin-bottom:10px; }
  h2 { .h3; }
}

h1, .h1 {
  font-size: 59px;
  font-weight: 600;
  line-height: 74px;
  text-transform: uppercase;
}

h2, .h2 {
  font-size:23px;
  line-height:30px;
  font-weight: 600;
  @media screen and (min-width:@size-desktop) {
    font-size: 37px;
    line-height: 40px;
  }
}

h3, .h3 {
  font-size: 17px;
  font-weight: 600;
  line-height: 25px;
  margin:15px 0 5px;
  @media screen and (min-width:@size-desktop) {
    font-size: 23px;
    line-height: 30px;
  }
}

header {
  background-color:@color-grey;
  box-sizing: border-box;
  .brand {
    text-align: center;
    color:white;
    font-size: 23px;
    font-weight: 600;
    line-height: 29px;
  }
  .tagline { display: none; }
  @media screen and (min-width:@size-desktop) {
    margin: 20px 20px 0;
    height: 220px;
    border: 1px solid @color-ltgrey;
    background-color: transparent;
    .brand {
      .h1;
      background-color: @color-grey;
      text-indent:-5px;
      margin:10px;
      height: 165px;
      box-sizing:border-box;
      padding-top:108px;
      text-align: left;
    }
    .tagline {
      display: block;
      margin-left:10px;
      margin-top:12px;
      font-weight:400;
    }
  }
}

img { max-width:100%; }

input {
  .input;
  &[type="checkbox"] {
    display: inline;
    width:auto;
  }
}

.intro {
  border-bottom:1px solid @color-ltgrey;
  padding:20px;
  margin-bottom:30px;
  .link-back {
    display: block;
    margin-bottom:10px;
  }
  .options { -top:20px; }
}

li {
  font-size: 14px;
  line-height: 20px;
}

main {
  @media screen and (min-width:@size-desktop) {
    padding:27px 20px 0 0;
  }
}

nav {
  background-color:@color-grey;
  .mobile-nav-link {
    display: block;
    .img-replace();
    background-image:url("../images/icon-menu.png");
    height:40px;
  }
  .nav-body {
    background-color:@color-ltgrey;
    padding:5px 20px;
    position: absolute;
    top:80px;
    left:0;
    width:100%;
    display: none;
    box-sizing: border-box;
  }
  .btn-close {
    position: absolute;
    top:110px;
    right:20px;
    .img-replace;
    border:none;
    padding:0;
    width:auto;
    background-image:url("../images/icon-x-dk.png");
    background-color:transparent;
    display: none;
  }
  &:hover {
    .nav-body, .btn-close { display: block; }
  }
  @media screen and (min-width:@size-desktop) {
    background-color:white;
    .nav-body {
      background-color: white;
      position: static;
      display: block;
    }
    .btn-close { display: none; }
    .mobile-nav-link { display: none; }
  }
}

p {
  font-size: 14px;
  line-height: 20px;
  margin:10px 0;
  max-width:760px;
  &.lead {
    font-size: 17px;
    line-height: 25px;
    font-weight: 300;
    max-width:760px;
    @media screen and (min-width: @size-desktop) {
      font-size: 23px;
      line-height: 30px;
    }
  }
}

.page-collection-public {
  main { padding:20px; }
}

.page-work-public {
  .link-back { margin-left:20px; }
  main { padding-top:20px; }
}

.public-header {
  border:none;
  width:auto;
  height:auto;
  h1 {
    padding:8px 10px 0;
    text-align: center;
    font-size:17px;
    line-height:25px;
    font-weight:400;
    text-transform: uppercase;
    color:white;
    @media screen and (min-width:@size-desktop) {
      padding:0;
      text-align: left;
      color:@color-grey;
      font-size:14px;
      font-weight:300;
      text-transform: none;
    }
  }
}

#prototype-nav {
  width:100%;
  max-width: 100%;
  margin-top:0;
  padding:5px;
  font-size:11px;
  background-color: @color-grey;
  color:white;
  text-align: center;
  box-sizing: border-box;
  a { color:@color-blue; }
}

.requirement-status {
  font-weight:400;
  &.status-not-started {
    color:@color-red;
    .progress-bar { background-color:fadeout(@color-red, 80%); }
    .progress { background-color: @color-red; }
  }
  &.status-complete {
    color:@color-green;
    .progress-bar { background-color:fadeout(@color-green, 80%); }
    .progress { background-color: @color-green; }
  }
  &.status-in-progress {
    color:@color-orange;
    .progress-bar { background-color:fadeout(@color-orange, 80%); }
    .progress { background-color: @color-orange; }
  }
  .label { font-weight:600; }
  .progress-bar {
    margin-top:15px;
    height:10px;
    overflow: hidden;
    max-width:100%;
  }
  .progress {
    .img-replace;
    min-width:1px;
    display: block;
  }
}

.select-students {
  position: relative;
  width:45%;
  &.reviewing .students { top:60px; }
  .label { background:right center no-repeat url("../images/icon-down.png"); }
  .selected-student {
    display: block;
    font-size: 23px;
    font-weight: 300;
    line-height: 30px;
    color:@color-blue;
  }
  .students {
    display: none;
    position: absolute;
    width:100%;
    top:30px;
    right:0;
    background-color: @color-ltgrey;
    box-sizing: border-box;
    padding:10px 0;
    a {
      display: block;
      padding:5px 23px;
      &:hover {
        color:white;
        background-color: @color-blue;
      }
    }
    &.on { display: block; }
  }
  @media screen and (min-width: 600px) {
    width:260px;
  }
}

textarea { .input; }

.tray {
  position: fixed;
  width:100%;
  height:100%;
  background-color:fadeout(@color-grey, 80%);
  display: none;
  &.on { display: block; }
  .content {
    width:300px;
    height:100%;
    position: fixed;
    top:0;
    right:0;
    background-color: white;
    padding:10px 20px;
    box-shadow:0 0 10px fadeout(@color-grey, 20%);
    h2 { .h3; }
  }
}

.work-images {
  padding:0 20px 20px;
  max-width:960px;
  li { background-size: cover; }
  img { display: none; }
}

.work-tile {
  border:1px solid @color-ltgrey;
  box-sizing:border-box;
  padding:5px;
  width: 170px;
  height:155px;
  position: relative;
  a { color: @color-grey; }
  .title {
    font-weight:400;
    display: block;
  }
  .date { display: block; }
  .image {
    max-width:100%;
    background-size: cover;
    height: 95px;
    img { display: none; }
  }
  .options {
    position: absolute;
    padding-top:0;
    top:10px;
    right:10px;
  }
  @media screen and (min-width: @size-desktop) {
    padding:10px;
    width: 260px;
    height: 220px;
    .date { text-align: right; }
    .image { height: 165px; }
    .options {
      top:20px;
      right:20px;
    }
  }
}

.work-tile-placeholder {
  padding:0;
  width:170px;
  height:155px;
  button { height:100%; }
  @media screen and (min-width: @size-desktop) {
    width: 260px;
    height: 220px;
  }
}

.work-tiles {
  padding:0px;
  li { margin:0 20px 40px; }
}

Here we also proceeded to set up box sizes for the tiles and did some minimal absolute positioning for a few elements:

Once again the result makes significant headway at applying the overall look-and-feel but simply lacks the layout and mircolayouts.

View the project to this point »

Now we can put our layout analysis work from phase 1 to use as we build out the various layouts and microlayouts for our site.

In the end we used a good mix of flexbox and the grid to complete this more complex multi-page build. We had a lot of elements in common between pages and we ensured that components all flex smoothly from the smallest device size up to large displays.

View the project to this point »