Template and Notes from my Deseret Digital Workshop

Chris Coyier //

Last week I was in Salt Lake City to do a workshop for Deseret Digital. Like my last workshop, we used the time to literally build a website from scratch. I think this live coding style is a decent way to do workshops because it is what real life web development is like. Nothing theoretical, all real working code. The following will be basic notes on what we covered and the template we built. While this certainly isn't as good of a learning experience as being there in person is, I figured it's still a useful thing to share with everyone.

Template

We built a simple website.

It's no thing of great beauty, but feel free to do what you want with it. It was more about covering lots of modern web design techniques n' stuff.

Notes

The logo is SVG. SVG is vector based so it maintains it's quality at any size (especially for simple shapes like this). It looks nice and sharp on my Retina laptop as well as the low resolution projector we were using, at any size we scaled it to. Not to mention SVG file sizes are typically small and compress well.

SVG is a native format of Adobe Illustrator, so logo.svg isn't some exported file that comes from a master document, it is the master document (as opposed to how png/jpg normally work). Using SVG is as easy as <img src="file.svg"> or background: url(file.svg);. Unlike icon fonts, a single SVG can have multiple colors and whatnot.

Not every single browser supports SVG. We looked at IE 8 and the Samsung Galaxy Tab in Browserstack (which runs real emulators in real VMs in your browser) and saw the SVG failing. So, we made a PNG fallback graphic. Then we downloaded a custom version of Modernizr and swapped out the src of the image if SVG wasn't supported.

if (!Modernizr.svg) {
  $(".logo img").attr("src", "images/logo.png");
}

We apply a box-shadow to the logo image, which applies a square shadow around the edge of the image box. Not what we wanted - we wanted the shadow to go around the edges of the stars. We are able to do that with filter: drop-shadow(); instead. Learn more about that important distinction. With SCSS:

.logo {
  img { // SVG
    @include filter(drop-shadow(2px 2px 2px rgba(black, 0.5)));
  }
}

Right from the get-go, we:

  1. Made a local domain name to work with through MAMP
  2. Gave ourselves a simple clean directory structure to work with: index.html, /scss/, /css/, /js/, /images/
  3. We watched that local folder with CodeKit so we could use Sass & Compass as well as get the style injection going which is a nice part of workflow.

The navigation is 8x1 at desktop-ish sizes, 4x2 at tablet-ish sizes, and 2x4 at phone-ish sizes. We made that happen easily through media queries in our CSS.

To be as smart as possible with media queries, we essentially "name" them with a custom user @mixin as outlined here. Then we can use them wherever without repeating ourselves as well as "nest" them with other style block declarations. This keeps the properties we are changing right next to each other in the code which is nice for our brains to keep track of everything.

@mixin mq($mq) {
  @if $mq == mama-bear {
    @media (max-width: 1250px) { @content; }
  }
  @if $mq == baby-bear {
    @media (max-width: 800px) { @content; }
  }
  // etc
}

Thus the navigation changes end up like:

.main-nav {
  font-size: 0;
  a {
      display: inline-block;
      font-size: 1em;
      width: (100% / 8);
      @include mq(mama-bear) { width: (100% / 4); }
      @include mq(baby-bear) { width: (100% / 2); }
  }
}

At our phone-ish media query (baby-bear), we don't show the navigation at all by default (it's too big and overwhelming). Instead we show a link to reveal the navigation. This we did without any JavaScript at all, but instead by using the :target selector in CSS and hiding/showing stuff as needed.

@include mq(baby-bear) {
  .main-nav {
    display: none;
  }
  #main-nav:target .main-nav {
    display: block;
  }
}

We structured the content into a grid. We used the Don't Overthink It Grids principals. This grid is entirely based on percentages, so it's inherently flexible. We also have the columns stop floating and become full width at the baby-bear media query.

.grid {
  padding: $pad 0 $pad $pad;    // $pad is a global spacing variable
  @extend .group;               // clearfix w/o needing a class
  background: rgba(white, 0.2); // fun little rgba/sass trick
}
.col-2-3 {
  width: 66.66%;
}
.col-1-3 {
  width: 33.33%;
}
.col-1-2 {
  width: 50%;
}
.col-1-4 {
  width: 25%;
}
.col-1-8 {
  width: 12.5%;
}
.col {
  float: left;
  padding-right: $pad;
  @include mq(baby-bear) {
    width: 100%;
  }
}

This is a good start in making a "responsive" design.


In our main content area, we're using some media: a Flickr photo and a YouTube video. In both cases, we used the copy-and-paste code provided by those services. In our flexy design, both the image and video have problems. At narrow widths they break out of the parent container. We fix image with some simple CSS:

img {
  max-width: 100%;
  height: auto;
}

That's not as easy with video which comes in an iframe and thus has no inherent aspect ratio. We enlist FitVids.js to help us here.

$("article").fitVids();

We now have a good bit of JavaScript going on: jQuery, Modernizr, FitVids, as well as custom JS we've written to use these things. That's four JavaScript files which is too many. All of the stuff we've written so far is likely "global" in that any page of the site should be running it. So, we make a global.js file and have CodeKit prepend all the libraries we're using to it (i.e. // @codekit-prepend "jquery.js"). Now we only load one JS file per page (global-ck.js), which is good for performance.

This is similar to what we are already doing with SCSS - breaking files into small chunks and @importing them into the global.css. Ultimately our global.scss file is just a bunch of imports and a little bit of generic global code.

@import "compass/css3";
@import "bits";
@import "normalize";
@import "toolbox";
@import "grid";
@import "module";
@import "fonts";
@import "typography";

* {
  @include box-sizing(border-box);
}

// Structure and basics

In our sidebar we want to have a map to our location. Google Maps provides code to embed interactive maps on websites easily. They work on mobile devices, but 1) they are iframes and thus slow and 2) they are a crappier experience than using a native app or maps.google.com.

This is a good candiate for conditional loading. Mobile device: load up a small graphic and link to the native app. Desktop: load the interactive iframe. We enlist Enquire.js to help us with this. Enquire.js runs JavaScript functions when media queries that we declare match or unmatch. So for us, we can Ajax in (via jQuery) the correct bit of content for the relevant media query.

enquire
  .register("(min-width: 800px)", { // mama or bigger
    match: function() {
      $("#map").load("parts/papa-map.html");
    }
  })
  .register("(max-width: 799px)", { // baby
    match: function() {
      $("#map").load("parts/baby-map.html");
    }
  })
  .listen();

The texture of the site is from Subtle Patterns. Their downloads now come with 2x versions for retina displays. We utilize this, but only reference the 2x version inside a retina media query so it overrides the original. We confirm that it only downloads one or the other through the Resources tab of Chrome Dev Tools.


We used Open Sans from Google Fonts. Instead of linking up another CSS file as they suggest, we open up that CSS file and snag out the @font-face rules and put them in our own typography CSS files.