Advertisement

Hi-Performance Web Design with Data URIs

by

This Cyber Monday Tuts+ courses will be reduced to just $3 (usually $15). Don't miss out.

We recently covered the The What, Why and How of Data URIs in Web Design on Webdesigntuts+, where we looked into some of the possibilities of using data URIs for performance-friendly interface designs.

In today's Premium tutorial, we're going to be taking some of the theory covered in the introductory article, building upon some of the examples used to develop a fully realized user interface. Just to keep things interesting, we'll also be making our design responsive and we'll be using the Dribbble API and jQuery to pull in some images from the Envato Dribbble feed.

There's a lot to cover, so let's get started!


Before We Get Started

As I've already mentioned, this tutorial builds upon the The What, Why and How of Data URIs in Web Design tutorial. Even if your knowledge of Data URIs is already solid, I highly recommend that you read this article before getting started here. I'll be using some of the code from the exercises in the initial article to save time, so I'll be asking you to jump back to the original article at a couple of points.

Secondly, we'll be relying heavily on data URIs in this project to give you lots of practice in using this technique. Whether or not I'd actually use data URIs this heavily in production is debatable (especially if supporting <IE7), so just remember that data URIs are simply another tool in your toolbox and these techniques are not necessarily a silver bullet for every situation.

Finally, this project is tested in the latest releases of Webkit, Firefox and Internet Explorer. You can safely assume that this interface won't be fully functional in IE7 and below (although fallbacks could easily be applied to support old IE releases, if required). Further, I'll be referring to -webkit and generic prefixes for CSS3 elements in the tutorial, but the source files will contain all vendor prefixes.


A Two Minute Review of Data URIs

Before we get cracking, let's take a (very) quick refresher on what the data URI scheme is and why it can be a good alternative to serve elements to the browser that traditionally can drain resources and slow performance.

  • Data URIs are a way of encoding binary data into an ASCII text string that modern browsers can read and render.
  • Data URIs are often used to replace image files, but any binary data can be encoded, including SVG, multimedia, fonts and more.
  • The primary benefit of using Data URIs is to reduce the amount of HTTP requests required to load the page in the browser window. Simply put, each additional HTTP requests (usually) negatively impacts overall site performance.
  • Data URIs are around 33% larger than a standard filetype (e.g. a JPG image), but with compression and gzipping, this amount is often reduced to under 5%. This additional file size is not normally a large problem, but is good to bear in mind when choosing the best technique to include assets into a design.
  • Data URIs are long, non-sensical and do not look very pretty at all, making markup and stylesheets difficult to read, navigate and maintain. It's a good idea to use code folding in your text editor liberally!

Plan of Attack

There will be three separate phases of this tutorial, that we'll tackle in the following order:

  1. Design the 'full-width' site using reference images in HTML and CSS.
  2. Pull in images from the Dribbble API and the jribbble jQuery plugin
  3. Add in media queries to make the site responsive at smaller viewports.

Okay! Enough talk! Let's get coding!


Step 1: Initial Markup

For our design, we're going to be starting with the latest HTML5 Boilerplate as a starting point. We're not going to be using all of the features of the boilerplate in the project (for example the modernizer script or analytics), however its a good idea to leave it 'as is' in the initial stages and return later in the project to pare down any features that we didn't end up using (or probably won't use in the future, either).

Create a new index.html file in a directory that also contains a css and js folder.

In the <body> of the document, add in a <header>, a featured content <div> and a <footer>. We'll be fleshing these out as we go on.

<!DOCTYPE html>
<!-- HTML5 Boilerplate content goes here -->
    <head>
        <title>Albireo: Designing With Data URIs</title>

        <link rel="stylesheet" href="css/style.css">
<!-- HTML5 Boilerplate content goes here -->
</head>
    <body>
    <!-- HTML5 Boilerplate content goes here -->
        <header>
            <!-- header content will go here -->
        </header> <!-- end header -->
        <div class="featured-content">
            <!-- featured-content content will go here -->
        </div><!-- end featured content -->
        <footer>
        </footer>
<!-- Scripts and analytics can go here -->
    </body>

Next, let's reset our CSS using Eric Myers CSS Reset and add in a few base layout styles:

* {
    /* Give all elements box-sizing of border-box
    (Total width = width + margin + padding + border) */
    -webkit-box-sizing: border-box;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
}

body {
  margin:0;
  padding:0;
  background-color: #eee;
}

header {
  height: 80px;
  margin:0;
  padding:0;
  position: relative;
  background-color: #3C3C3C;
}

.featured-content {
  margin-top: 4%;
}

footer {
  border-top: 1px solid #eee;
  width:100%;
  min-height: 80px;
  background-color: #ccc;
  margin-top: 0;
  text-align: center;
  padding-top:21px;
}

Everything here should be fairly straightforward, with the exception of the universal application of the border-box box model. Here, instead of using the standard box model where the padding, margin and border is added separately to the defined width of the element, the defined width is the total width of the element, comprising of padding, margin and border.

This approach is popular for fluid layouts and many of the popular frameworks use the border-box model, including Twitter Bootstrap and Zurb Foundation as it makes working with percentages dead easy. Need equal width columns with gutters between each? Easy. Just apply the boder-box model and set the column to 33.333% — with no need to separately calculate the padding and margins.

Of course, there's not much to see so far, this is just our canvas!


Step 2: Define a Simple Fluid Grid

If you take a look at the demo, you'll see that the featured content is going to be laid out in three equal-width columns. Behind the scenes, these columns sit inside a <div> with a class of <class="row"> that sets a centered content area with a max-width of 1024px.

So that we can proceed smoothly with the rest of our project, let's define this simple grid in our CSS (adapted in part from Zurb Foundation 3.1):

.row {
  width:1024px;
  margin:0 auto;
  max-width: 100%;

  /* Prevent child elements scaling down further once the viewport hits 767px */
  min-width: 767px;
  background-color: transparent;
}

.row:after {
  /* Clearfix applied since columns are floated */
  content: "";
  display: table;
  clear: both;
}

.row .three {
    /* Remember that the width of this element is the total of the defined width + padding + margin + border */
    width: 33.33333%;
}

.column, .columns {
    float: left;
    min-height: 1px;
    padding: 0 15px;
    position: relative;
}

Let me clarify a couple of points in this block of code before we move on. Firstly, you'll see that we've applied a min-width of 767px to the row. This means that when viewed on viewports smaller than this width, any child elements (i.e. .row .three) will stop scaling to prevent the featured content items becoming too small. We'll be returing to our media queries to give the browser additional instructions for when the viewport is smaller than 767px wide a little later in the tutorial.

Secondly, because we know that every row will contain floated child elements, we will need to clear all floats after the closing row div tag. In this project, I've applied the clearfix hack to the row:after pseudo element. If you intend to build this project out, and you may need additional clearfixes, just append the .row:after rule with an additional class of .clearfix:after.


Step 3: Define Typography and Icon Fonts

While we haven't come across any font use as yet, I prefer to establish my typography towards the top of the stylesheet (although there's nothing stopping you from tackling this later). So that we can swing straight into the design without backtracking, let's set all the base rules for our typography and icon fonts now.

For our custom, base64 encoded icon font, we'll be using the excellent IcoMoon app to generate our icon font set and the required CSS. For a complete walkthrough with screenshots, refer to the Data URI tutorial.

To summarize, using the IcoMoon app, create the following icon font:

  • Select a custom set that includes the 'heart', 'eye', 'comment' and 'magnifying glass' icons from the Broccolidry family.
  • Change the Baseline setting to 10%
  • Under the 'More Settings', change the font name to 'customFont'
  • Tick the checkbox against 'Base64 Encode & Embed Fonts in CSS'

Download the font, open the main stylesheet and copy the CSS across to the project's style.css file.

Along with some additional font styling, your Typography section of your stylesheet will contain the following code:

p, a, h1, h2, h3, h4, h5, h6, li {
  font-family: sans-serif;
}

a {
text-decoration: none;
}

@font-face {
  font-family: 'customFont';
  /* IE9 Compatibitly mode */
  src:url('fonts/customFont.eot');
  /* Old IE Fix */
  src:url('fonts/customFont.eot?#iefix')
}
@font-face {
  font-family: 'customFont';
  src: url(data:font/woff;charset=utf-8;base64,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) format('woff');
  font-weight: normal;
  font-style: normal;
}

/* Use the following CSS code if you want to have a class per icon */
[class^="icon-"]:before, [class*=" icon-"]:before {
  font-family: 'customFont';
  font-style: normal;
  speak: none;
  font-weight: normal;
  -webkit-font-smoothing: antialiased;
}
.icon-search:before {
  content: "\21";
}
.icon-comment:before {
  content: "\22";
}
.icon-heart:before {
  content: "\23";
}
.icon-eye:before {
  content: "\24";
}

/* Use the following CSS code if you want to use data attributes for inserting your icons */
[data-icon]:before {
  font-family: 'customFont';
  content: attr(data-icon);
  speak: none;
  font-weight: normal;
  -webkit-font-smoothing: antialiased;
}

For a more detailed explanation of the syntax of this code, its usage and the old IE fix, refer to the intial Data URI tutorial.

Now that we've set all of our groundwork including a basic fluid grid and a base64 encoded custom icon font, let's start moving through each section and giving our basic markup some much needed polish.


Step 4: Give The Body a Repeating Background

In the first tutorial, we created a Data URI from a small image from Subtle Patterns that will be perfect for our site's background. Take this base64 encoded image and add it to the <body> element of our page as a repeating background-image.

Flesh out the body rule with the following CSS, taken from the earlier steps in this tutorial:

body {
  margin:0;
  padding:0;
  background-image: url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAABkAAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjFBNjg5RjI2RjZCODExRTE5Q0IwRDA0QjY4QTg3NEJCIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjFBNjg5RjI3RjZCODExRTE5Q0IwRDA0QjY4QTg3NEJCIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MUE2ODlGMjRGNkI4MTFFMTlDQjBEMDRCNjhBODc0QkIiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MUE2ODlGMjVGNkI4MTFFMTlDQjBEMDRCNjhBODc0QkIiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAgICAgICAgICAgIDAwMDAwMDAwMDAQEBAQEBAQIBAQICAgECAgMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwP/wAARCAAsACwDAREAAhEBAxEB/8QAbAABAQEBAQEBAAAAAAAAAAAAAwIBAAQFCgEBAAAAAAAAAAAAAAAAAAAAABAAAwACAgEDAgQDCQAAAAAAAQIDEgQRExQhIiMAJEEyMwVCNBUxokOjVGQ1ZdURAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AP2JIiaaPGLokUQPKThimmhbEKoXjHUXglEJUIFKqQgAQJpNdHXraLfKHN6vbN23bOEnjbqm9WrYKs59aMU4RUQqBMhkgP3CNfKmVBoUOoxK11Gi60maOhBG6rKtFpNsUOJkzDirgpBpMQbYPDM8mtMmVqiY900eYRZXIBDtMgjFigU/kANmrft6xXUgtU6zNNGSuvXOEvbWCwlUz14AKrqEPtI6waYyqHp9fG7fKTnDv8r08fjHLPDsx8XH8M+cfXLL3/QDOmvoq0bVcV/WetyHtus7zl3L1ootVquk+uajAsiKgUzBCZSprUUlS2mxLSkxDU/baMCCqkEq+m6ngAcnXJKqTEgSCtiddmnXEmMwMdnYRmnayqSRqa7r6z5YnO3q01JWfFGLyDmrqVkNNVGR5iNRD01gYCTEnqIbVXWDzZaKRxkhQktPIL1+2Jee0wo4Hs3eFTyYoCVF1ULOOzIE5qoWb+roF5ZJgP3XZ29EfB7MvGwr5mXOfnfqdXHZ6+P19nHyZ9nxfQUJR/cBPa7aEKS2oULxbUqpKPQzoqsu6CGnRaLyi5SK8NUOGz2KbNTGfAnEsmxsryq2tNsX19QElj1n9Z+Ss2PWpagcyDdjYbTbsp79V+S7k+7Tb0+WvoWOgWIDuATAnJviyaQYdZNdn3mtjcKW2dir4ReCAt00VmM462uOTMj1kSzckvXsBNelNrKzq8InlYxfKewykel9lPbTXainlJHh0UguA5wmBdu73eL1jHn/AJHOGHXjl1ePl3f1Hj8MOjD5Mufh+gUGdDsNAMw5M7NJxMVtIibpNvQG8lQzZwV4ICFuV9gHVpV1FXTHLcFNQRIgYVjzLklpONZdVgVorTbjgoUYnrYO1i2ulm3nUXXOuxssQkHimTLSOR419aMzwZkkyPOTOT20DiYShOlJtPTm3Yq09qa6Li0q1kyhpQkwyAb0iOCQoX2BO6mzR1Gi3VsKrCtmYLLqKNjFw0bh7s7AzbBuk8uwZSZWBvh8XDqrxxh0cnye7nLHs7OfIz93bnxz8mfHu+gCuyv7e0NRYzwqqy0UmZQnPF5QWFV9BDXmaoFdVK+onx2GS1BvHMattTcB6AeanokdnBQq3CkkS2YzUKr8++ahHJxRphtNfyqK9m5jB84xUgq2xNvZs3HqtG16LzJDyqOA5BcJ1gKbr02W0QqrsSya1QyGXUoiweK5M7XcXTKZHMcgzkq0jYFjEaIMpH7T3GMSyjxeFLGECxUeJwpwTn4fyr8eKzCfFv2+X5NfJ/s6e2ng9H+m8f8ATy593kY92f49fw/QL9tztduHOH3Xfjx4/FMM8vb4uOfH8POXPuz+g83yeVDt7PB6U8bLnLzO2n89n8nHV1+Pz6dmfZ8nV9BVe7zZ+Lzjw/8AUfy9eHQ3j9WXp/Ue7Dj+Doy7PXp+gX7Xplhxx2now57vJ5p2Y5e/yOc+3L14z7PTL6CNXt79ny/5ntr08foeD2fb+N+OXXj5GXv7uf8AD6foPn/deV/0H+f3/wDgf38/9r9B/9k=);
  background-repeat: repeat;
  background-color: #eee;
}

Great! We now have a sweet looking background tile that repeats on both the y and x axes over the entire document.


Step 5: Markup and Style the Site Header

Hop back into your markup and add the following HTML in the <header> section:

<header>
<div class="row"><a class="logo"><img src="css/logo.svg" alt="Albireo" />Albireo</a>
<nav>
<ul>
	<li><a href="#">Home</a></li>
	<li><a href="#">About</a></li>
	<li><a href="#">Categories</a></li>
	<li><a href="#">Submit</a></li>
</ul>
</nav></div>
</header>

Next, let's style our header background with a plaid background that is rendered using repeating linear gradients:

header {
  height: 80px;
  margin:0;
  padding:0;

  /* We'll be positioning elements absolutely inside the header shortly */
  position: relative;

  border-bottom: 1px solid #303031;

  box-shadow: inset 0px -1px 14px rgba(0, 0, 0, .6), 00px 5px 10px rgba(0, 0, 0, .1) inset, 0px 1px 1px white;

  /* Left to right linear gradient */
  background-image: -webkit-repeating-linear-gradient(45deg,
  transparent 0%, transparent 60px,
  rgba(54, 54, 54, 0.7) 60px, rgba(54, 54, 54, 0.7) 100px,
  rgba(0, 255, 199, 0.1) 100px, rgba(0, 255, 199, 0.1) 140px,
  rgba(172, 172, 172, 0.1) 140px, rgba(172, 172, 172, 0.1) 160px),

  /* Right to left linear overlayed gradient */
  -webkit-repeating-linear-gradient(-45deg,
  transparent 0%,transparent 60px,
  rgba(54, 54, 54, 0.7) 60px, rgba(54, 54, 54, 0.7) 100px,
  rgba(0, 255, 199, 0.1) 100px, rgba(0, 255, 199, 0.1) 140px,
  rgba(172, 172, 172, 0.1) 140px, rgba(172, 172, 172, 0.1) 160px);

  /* Provide a fallback background-color */
  background-color: #3C3C3C;
}

This background is a modification of a technique that was employed in a recent Webdesigntuts+ article. For more information, refer to The What, Where and How of Textures in Webdesign.

Next, let's add our .svg logo which you can download from the resources section of this tutorial.

.logo {
  width: 175px;
  height: 80px;
  margin-left: 15px;
  top: 5px;
  position: absolute;
  background-repeat: no-repeat;
  display: block;

  /* Render text invisible */
  font-size: 0;
  color: transparent;
  text-shadow: none;
}

.logo img {width:100%}

You'll note that I've referenced the logo as an external .svg file within the HTML markup (with the additional HTTP request) for a completely scalable vector solution. For true image free design, you could also use the .svg file directly in the HTML itself. To read how best to do this, refer to Getting Started With Scalable Vector Graphics (SVG).


Next, we'll style a simple navigation menu for the site:

nav {
  top: 23px;
  position: absolute;
  right: 30px;
}

nav ul li {
  display:inline-block;
  font-weight:bold;
  font-size: 13px;
}

nav ul li a {
  color:#eee;
  display:block;
  padding:10px;
  height: auto;
  border-radius: 6px;
  cursor: pointer;
  margin-right: 5px;
}

nav ul li a:hover {
  background: -webkit-linear-gradient(top, rgba(0,0,0,.4) 0%, rgba(0,0,0,0.6) 100%);
  color:white;
}


Step 6: Style a Simple Footer

We're going to break our logical flow just for a moment and take care of the styling of the (very simple) footer, since the bulk of the coming steps are going to be focused on creating the content items, and I want to make sure that we maintain momentum through this section. Add the following markup to your HTML:

<footer><a href="#">Designing With Data URIs | Webdesign Tuts+ 2012</a></footer>

The styling as follows:

footer {
  border-top: 1px solid #eee;
  width:100%;
  min-height: 80px;
  background-color: #ccc;
  margin-top: 0;
  text-align: center;
  padding-top:21px;
  box-shadow: inset 0px 4px 10px rgba(0, 0, 0, .2);
}

footer a {
  color:#999;
  font-size: 14px;
  font-weight: 100;
}

With that done, let's return to look at the focal point of the design, the featured content items.


Step 7: Featured Content Items

In the next phase of the tutorial, we'll be pulling images from Dribbble using jQuery and the Dribbble API. For now, we are just going to use a placeholder image and set the general style of the images themselves.

It's important to note that our markup will be changing to accommodate the jQuery plugin very soon, so don't get too attached to the following markup! With that said, if you don't want to pull resources from an external site and serve your own static assets, this is the code that you'll be using.

We'll design the interface to be able to be extended to accommodate as many images as required just by replicating the entire <figure> element. We will start by creating a single <figure> element and then replicating it five more times to create two rows of three images.

In your HTML, add in the base markup for the featured content image panels directly under the </header> tag:

<div class="featured-content">
  <div class="row">
    <div class="three columns content-items">
      <figure>
        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
        <img src="img/1.jpg">
        <ul class="icon-group">
          <li><a href="#"><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number">20</span></a></span>
          <li><a href="#"><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number">8</span></a></span>
          <li><a href="#"><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number">4</span></a></span>
          <!-- the list-number values are purely arbitrary -->
        </ul>
      </figure>
    </div>
    <div class="three columns content-items">
          <-- 2nd featured content panel will go here... -->
    </div><!-- end three columns -->
    <div class="three columns content-items">
          <-- 3rd featured content panel will go here... -->
    </div><!-- end three columns -->
    <div class="three columns content-items">
          <-- 4th featured content panel will go here... -->
    </div><!-- end three columns -->
    <div class="three columns content-items">
          <-- 5th featured content panel will go here... -->
    </div><!-- end three columns -->
    <div class="three columns content-items">
          <-- 6th featured content panel will go here... -->
    </div><!-- end three columns -->
  </div><!-- end row -->
</div><!-- end featured-content -->

Summing up what we've done here, we've created three columns of equal width (33.3333%). Inside each column, we've created:

  • a <figure> element that houses the image,
  • an anchored link that will take us to additional content (i.e. Dribbble)
  • a span element within the link that will define our magnifying glass icon
  • a image tag with no set source (we'll populate this with jQuery in the next steps)
  • an unordered list of icon fonts for 'comments', 'views' and 'likes', that have been given some arbitrary number values.

Because the placement/flow of the images is not critical to the overall document structure, the HTML5 <figure> element is the perfect wrapper for the <img> element.

Moving on to the styling of the content items, we'll start by adding a margin to the bottom of the content item rows and then style the <figure> element. The <figure> consists of one element and two pseudo-elements to give the effect of a stacked deck of images.

To create a interface that better responds to changing browser window sizes, both horizontally and vertically, we'll use percentages to measure the width and height of the element wherever we can.

The code required to style the figure elements and the image stack effect is as follows:

.content-items {
  margin-bottom: 5%;
}

figure {
    /* Max height of any image at any viewport width */
  max-height: 260px;
  width: 100%;
  margin:0 auto;
  padding: 3% 3% 30px 3%;

  display:block

  /* Allow children to position themselves absolutely to the parent */
  position: relative;

  background: -webkit-linear-gradient(top, white 0%,#F3F3F3 100%);
  background-color: #fff;
  border-radius: 5px;
  border: 1px solid #C7C7C7;
}

/* Bottom-most 'stacked' image */
figure:before {
  content: '';
  height: 100%;
  width: 98%;
  margin:0 auto;
  /* Absolute position within the relatively positioned parent */
  position: absolute;

  /* Stack bottom-most */
  z-index: -2;
  top: 6px;
  left: 3px;

  border-radius: 5px;
  border: 1px solid #C7C7C7;

  background: -webkit-linear-gradient(top, white 0%,#ECECEC 100%);

  background-color: #efefef;
  box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.1);
  }

/* Top-most 'stacked' image */
figure:after {
  content: "";
 height: 100%;
   width: 99%;
  position: absolute;
  top: 3px;
  left: 1px;
  /* Stack to middle */
  z-index: -1;
  margin: 0 auto;

  border: 1px solid #C7C7C7;
  border-radius: 5px;

  background: -webkit-linear-gradient(center top , white 0%, #F3F3F3 100%) repeat scroll 0 0 #EFEFEF;

  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

We should now have a nice stack effect that scales nicely when the browser window is resized from full-width down to 767px.


Step 8: Create Placeholder Images

Next, let's style our the actual image itself. In the next stage of the tutorial, we'll be populating this area with assets directly from Dribbble, but for now, we'll add in a placeholder image (a gray Envato logo) which will ensure that our <figure> element doesn't collapse upon itself while images are being loaded. Additionally, the placeholder image will also keep the design functioning if there are ever any problems with fetching the content from Dribbble in the future (although we will want to keep the default broken image icon present to give some feedback to the user that there should be an image).

For the placeholder image, I've converted a 291px x 218px .jpg image (contained in the source files) into a Data URI using the Web Semantics Data URI tool.


After copying the base64 encoded string from the tool's output panel, we can complete the styling for the image element:

figure > img {
    max-height: 219px;
    /* Super simple responsive images */
    width: 100%;

    margin:0 auto;
    padding: 0;
    border: 1px solid #E4E4E4;

  /* Placeholder Image */
  background-position:center center;
  background-image: url(data:image/jpeg;base64,/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMraHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENlaGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjMtYzAxMSA2Ni4xNDU2NjEsIDIwMTIvMDIvMDYtMTQ6NTY6MjcgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHhtbG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjNGRUMwNEREMEJCMDExRTI4RUE0QzM4NTc5MUMxNTFBIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjNGRUMwNERFMEJCMDExRTI4RUE0QzM4NTc5MUMxNTFBIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6M0ZFQzA0REIwQkIwMTFFMjhFQTRDMzg1NzkxQzE1MUEiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6M0ZFQzA0REMwQkIwMTFFMjhFQTRDMzg1NzkxQzE1MUEiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7/7gAOQWRvYmUAZMAAAAAB/9sAhAAGBAQEBQQGBQUGCQYFBgkLCAYG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);
}

By giving the image a width of 100% and a max height of 218px, we are ensuring that the images maintain their aspect ratio while scaling down relative to the <figure> element as the browser window is resized. There are more sophisticated approaches to responsive media, but this super-simple approach will work just fine for this application.

For a quick sanity check, this is what you should have so far for the first <figure> element:



Step 9: Style the 'View More' Link

When each of the images is hovered over, a semi-transparent overlay will appear on top of the image, along with a magnifying glass, that we'll be creating with our icon font.

First, let's quickly review the markup for the view more link:

<figure>
<a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
<img src=".">
...
...
</figure>

We'll be styling a block level anchor link that contains a 100% width and 100% height span with has a class of icon-search which will hook into our icon font CSS that we've already established.

The code for the link looks like this:

a.view-more {
   max-height: 219px;
  display: block;
  position: absolute;
  z-index: 20;
  top: 3%;
  bottom: 31px;
  left: 3%;
right: 3%;
border: 1px #464646 solid;
  background-color: rgba(0, 0, 0, 0.5);
  -webkit-box-shadow: inset 0px 2px 76px rgba(0, 0, 0, .6);

  opacity: 0;
  -webkit-transition: .3s all;
}

Next, we'll add in pseudo-classes to style the link on :hover and :active:

a.view-more:hover {
  opacity: 1;
  -webkit-transition: all 0.3s;
}

a.view-more:active {
  opacity: 1;
  background-color: rgba(0,0,0,0.7);
}

You'll note that for the sizing and positioning of the view-more link, percentages are used by default. This approach allows the view-more link to scale and maintain a nice ratio as the browser window is resized.

Also, you'll note that a transition is fired on the a.viewmore link itself. While this doesn't do anything by itself, it allows the link to fade out when the user hovers and then moves off the element.


The CSS for the magnifying glass span is established through the use of two separate classes, large-icon and icon-search. The code for the first class is as follows (which I've included in the 'Typography' section of the stylesheet):

.large-icon {
  width: 100%;
  height: 100%;
  position: absolute;
  display: block;

  font-size: 76px;
  color: rgba(255, 255, 255, 0.9);
  margin: 0 auto;

  /* Vertical and horizontal alignment */
  padding-top: 25%;
  text-align: center;

  /* Give the icon a white glow effect */
  text-shadow: 0px 0px 70px rgba(255, 255, 255, 1);
  -webkit-transition: all 1s;
}

And we've already established the CSS for the icon-search earlier in the tutorial using the generated code from the IcoMoon app. Just to refresh your memory, however, here's the relevant code that will serve up the unstyled magnifying icon icon:

@font-face {
  font-family: 'customFont';
  src: url(data:font/woff;charset=utf-8;base64,d09GRk9UVE8AAAbAAAsAAAAACjQAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAABCAAAA5kAAAVu0W1y/EZGVE0AAASkAAAAGgAAABxicFXOR0RFRgAABMAAAAAdAAAAIAAzAARPUy8yAAAE4AAAAEsAAABgT/7cGmNtYXAAAAUsAAAARAAAAVICPfbNaGVhZAAABXAAAAAuAAAANvoGJzNoaGVhAAAFoAAAAB4AAAAkA/3/1GhtdHgAAAXAAAAAEAAAABgKAAAAbWF4cAAABdAAAAAGAAAABgAGUABuYW1lAAAF2AAAANwAAAF6Arsl/nBvc3QAAAa0AAAADAAAACAAAwAAeJxtU1toFGcUPrM7sztdNptok7gx466psUihGE168UHTavGhlKYSpaUUMTYtVuKFxIhg6kOjiPwGkWxo0QctiL0QSigl1VJKEAkqsrVgamPSbiFJFWKopcad7Sb5es4/kw2FzsPH+c/5zv2MQaZJhmGE321vO7BvDxkBMmiD6wTcZUE3bqpoUEXNRIQqGhdDqYIQDas33R63yXKordghKnHoyCKHbMd6fzEFJYZNMSpt3/vB5pqaGi+2n4G/AAXJJMs4TiGhBoz9gY+Ck2ba/CUGI7//AjA70FkGvNfwO+hMX5UFzG15ADK+fpYlM87S0vNMGtsKooFO0JLVS0HJ77aDiu5EQE9PX1GgZ7Y8sEG77bXAlwy0LZsGejz4UXRrPMOCjim+gd3WJHQIpcP5gXWKJasrJe1RvwAjcUmKqmDpq1IpdNKWopdL+RkLFIqngNy9PaAnxrZytJDTK8puZpWziaHbZ4lJaZo4tPisuaun3oFxqr6jDLSZw9I6ngoGJcFlkS43ZPznYF/Vf3VVQs6IW1XBN+MFEF0yluWkRUf+4chdOzaCWs/9AGpe9Rh0uKvBA2hDc/VvgFg9EB3O8kBw9OD1pAJ6J5pAtfn7wLef3gC9lrsLDLecBKYbXSDT/DbwcPlVYEKe97Thixkg3zHF8NRPDCMrQIFv1gGYPAbMdLfzOFs48iyvnOzoq0B2VyvIkgC59F1QUFaVr+8AFW/qAZnbnwctGvwcFD7xAmjVpUY2lB8HLfu7nyG0EuTIpcSdXh58pPQwb2rqNHNqa4FHclyRna8rO+aa7stlKsxLP3iNd1HNw8BfoWpQZW0dMH4zzRIfG0s3QbHZR8DInQjXOXziRcGuNzw6RrghjN+eY7g1YQMv/VoD1Nd/aHF3uT/PcTvcBGY+7mLp0G0lOCSYHubWdrUd4+WX7BS/4XLQwECnJdEgySs5Q2S9lDYtt7n3E6WL9arRBesKdcHzsFbTmZIbFfJ1NY+JWxOF/ngQ49f6bdaSWRHWyOvgpbIUF+mPBR3PsPjA98BU/yueRE8e+lkTxZ3XJWBJJY6fP8GzoCLZnEzW5tNzDbk7vg1qPXsxX6e4fX7lecz89oEVWbEIJVun6cl8ygQ9F4jy7ykda9BPAQzNG3CjAEPzhgWdRxny3ZLZOo654q0SvpLPHgJjLGFUpNHC8390muy5FXwLumRMuU3mTMpNhWNuqlSVpaKRfwFhPdinAAAAeJxjYGBgZACCk535hiD6THGxH4wGAECdBfQAAHicY2BkYGDgA2IJBhBgYmAEQlYgZgHzGAAEgQA4AAAAeJxjYGZiYJzAwMrAwejDmMbAwOAOpb8ySDK0MDAwMbAyM8AAowADAgSkuaYwODAofmBgPPv/LIMe41kG4wagGrgCBSBkBAD0Yws7AHicY2BgYGaAYBkGRgYQ8AHyGMF8FgYDIM0BhExgGZUPDP//g1mKENb/BwKsUF1gwMjGgMzFDhiZmFkIqRnCAAAJKAj+eJxjYGRgYABic6XVj+L5bb4ycDMxgMCZ4mI/BP3/LBMD41kgl4MBLA0AHxYKOwAAeJxjYGRgYDz7/yyDHhMDA8M/BiAJFEEBbABpdgPTAAB4nGNiYGBgQsMgAAAAuAALAABQAAAGAAB4nG2OQU7DMBBFX9o0CFGxg7VViWUiO7t2jXIAFt1XVRRVKrHkJFfhBOw4BgfgBNyF78QLFrU1nufx9/wBtnyQEVdGwWPiFXe8JF6zY0icS/OZeMMD34kL7V8ps/xele38K/JKHZ8Sr3mlTJzPvgtveOYrcaH6D2cmOY543uE8DaNXfqOl08OVE0HXtpuuJ0EjWS9xzEGKFkNNhVU+KP43WypObyV7RS1yOml8PzY+dK2pK2sOZjEVOFvuy9o6aW5OdZRfUPkyTxF7R2eObRguvjeusrc//gH1qDa7eJxjYGbACwAAfQAE) format('woff');
  font-weight: normal;
  font-style: normal;
}

/* Use the following CSS code if you want to have a class per icon */
[class^="icon-"]:before, [class*=" icon-"]:before {
  font-family: 'customFont';
  font-style: normal;
  speak: none;
  font-weight: normal;
  -webkit-font-smoothing: antialiased;
}
.icon-search:before {
  content: "\21";
}

It's important to note that we've tried to seperate the presentation and the styling of the icon as much as possible (infact, this is what I've tried to apply liberally throughout the entire design). This is a small project, but if we wanted to expand the scope of the interface and reuse the magnifying glass icon somewhere else in the design, this object-orientated approach will reduce the complexity of changing the styling of the icon.


Step 10: Style the Comments/Like/View List

All that's left to do now is to style our icon-group with the appropriate icon fonts.

Let's just remind ourselves about the markup for this part of the project. Each content item has the following unordered list:

<ul class="icon-group">
	<li><a href="#"><span class="list-icon" data-icon="&quot;"><span class="list-number">20</span></span></a></li>
	<li><a href="#"><span class="list-icon" data-icon="#"><span class="list-number">8</span></span></a></li>
	<li><a href="#"><span class="list-icon" data-icon="$"><span class="list-number">4</span></span></a></li>
</ul>

You'll note that while we've used a class name for our magnifying glass icon in the previous step, we're using the data-icon approach for this list. There's no particular reason for using this approach over the class based application — it's just a good opportunity to use the alternative execution of icon fonts. You'll see that we've used the Unicode given to us by the IcoMoon app to define the appropriate icon. Let's style the list itself first:

.icon-group {
  position: absolute;
  bottom: -1px;
  right: 11px;
  z-index: 30;
}

.icon-group li {
  float: right;
  margin: 0 0 0 12px;
  color: #AAA;
}

The first section of the code above positions the <ul> element, while the styling for the list items floats them to the right, displays the element inline and sets some parent-level styling. Next, we'll style the list-item and list-number elements:

.list-icon {
  line-height: 2;
  color:#888;
  position: relative;
  font-size: 16px;
  margin-right: 2px;
}

.list-number {
  line-height: 2;
  text-decoration: none;
  font-size: 12px;
  margin:0 2px 0 4px;
}

And with that, we've styled our first featured content element with some cool CSS3 and two different implementations of icon fonts. Here's our final content-item piece, shown with the view-more link in a hovered state:


At this point, if we were to keep the interface static (i.e. not pull in assets through the Dribbble API), we could simply copy and paste the content-items code block as many times as required and reference an appropriate image file. Since we're doing something a little different in the coming steps, however, we're going to leave our markup as is (for the moment), before talking about the next steps and — gulp — break apart all of our work so far.


Step 11: Introducing the Dribbble API and the Jribbble Plugin

For the next steps of the tutorial, we are going to be populating our featured-content items with shots taken directly from Dribbble by way of the Dribbble API.

The API allows us to take content directly from the application via HTTP, with responses returned as JSON. At the time of writing, the API doesn't require a key and has a fairly liberal non-commercial license with a few restrictions, so is perfect for our application.

If you've never tackled an API before, don't worry. We're going to be using the Jribbble jQuery plugin to help request a shot via an ID and slot it into the DOM with our custom layout that we've already designed. As I've mentioned already, we'll be requesting individual shots (for the purpose of the project we'll be taking six shots from the Envato Dribbble account) via the Jribbble plugin and the Dribbble API.

As you'd expect, along with individual shots, Jribbble also allows you to request a range of parameters including:

  • Get a shot
  • Get the comments of a shot
  • Get the rebbbounds of a shot
  • Get a list of shots by the list name
  • Get a list of a player's shots
  • Get a list of shots a player is following
  • Get a list of shots a player likes
  • Get the profile details of a player
  • Get the followers of a player
  • Get the players a player is following
  • Get the draftees of a player

I'm not going to spend any time today going over the plugin or the API in any significant detail, but if you wanted to expand the scope of this tutorial and explore some of the possibilities of Jribbble, I encourage you to read the documentation to learn more. For now, head over to the lab and download Jribbble and save it into a new root-level directory called 'js'.


Step 12: Link Up The Scripts

The first thing that we'll need to do to get Jribbble working is to hook up the relevant scripts. The plugin is jQuery dependent, so to get our plugin to work, we need:

  • the latest distribution of jQuery,
  • the Jribbble code,
  • a file called js/jribbble.shots.js which will be the home for all of the functions required to call the the Dribbble shots.

At the bottom of your HTML file, between the </footer> and </body>tags, add the following code:

<script type="mce-text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script type="mce-text/javascript" src="js/jquery.jribbble-1.0.0.ugly.js">
</script><script type="mce-text/javascript" src="js/jribbble.shots.js"></script>

Step 13: Get a Shot

The basic structure of the Jribbble 'Get a Shot' parameter is pretty simple:

$.jribbble.getShotById(shotId, callback)

Plug in a shot ID from Dribbble and the following data will be snagged by the plugin:

  • The shot itself (400px x 300px)
  • The shot title
  • The player
  • Views
  • Comments
  • Likes
  • Rebounds

Let's take a look at the demonstration function used on the Jribbble page to see how this information is requested and inserted into the DOM:

$(document).ready(function () {		
		
	// API Ref: http://api.dribbble/shots/:id
	$.jribbble.getShotById(196071, function (shot) {
		var html = [];
		
		$('#shotById a:first').attr('href', shot.url);
		$('#shotById img').attr('src', shot.image_url);
		$('#shotById h3').text(shot.title);
		$('#shotById h4').html('by <a href="' + shot.player.url + '">' + 
			shot.player.name + '</a>');
		
		html.push('<li><b>Views:</b> ' + shot.views_count + '</li>');
		html.push('<li><b>Likes:</b> ' + shot.likes_count + '</li>');
		html.push('<li><b>Comments:</b> ' + shot.comments_count + '</li>');
		html.push('<li><b>Rebounds:</b> ' + shot.rebounds_count + '</li>');
		
		$('#shotById ul').html(html.join(''));
	});
});

This is should be fairly straight forward, even if you're not a jQuery wizard. What the plugin is going to be looking for is an element with the ID of "shotById" and will then insert the the shot, views, likes, comments and so on into the DOM with the following markup:

<div id="shotById">
	<a href="#"></a>
	<img src="...">
	<h3>Shot Title</h3>
	<h4><a>Player URL</a></h4>

	<ul>
		<li><b>Views</b></li>
		<li><b>Likes</b></li>
		<li><b>Comments</b></li>
		<li><b>Rebounds</b></li>
	</ul>
</div>

Obviously, this won't work (as is) for our existing HTML markup that we've already created in the previous steps. To make sure that our hard work isn't destroyed, we're going to need to cannibalize our markup and make some changes to the base code in our js/jribbble.shots.js file.


Step 14: Shake Up the Markup

The first thing that I'd recommend is to take the existing content-items markup that we've already created, duplicate it and comment it out, to make sure that we have a reference point for the following steps. Next, overwrite the non-commented HTML with the following code:

<!-- REFERENCE ONLY (ORIGINAL MARKUP)
    <div class="three columns content-items">
        <figure>
            <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
            <img src=".">
        
                <ul class="icon-group">
                    <li><a href="#"><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number">4</span></a></span>
                    <li><a href="#"><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number">8</span></a></span>
                    <li><a href="#"><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number">20</span></a></span>
                </ul>
        </figure>
    </div> -->

    <div class="three columns content-items">
    	<!-- give the figure element a unique ID -->
        <figure id="shot1">
            <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
            <img src=".">
            <ul class="icon-group"></ul>
        </figure>
    </div>

As you can see, we've really pared down the markup in our index file. We'll be getting jQuery to fill in the blanks in the next step.


Step 15: Create a New Function

To call the shot and populate our <figure> element with some data, we need to create a new function in our newly created js/jribbble.shots/js file. Our new function looks like this:

$(document).ready(function () {	

$.jribbble.getShotById(332826, function (shot) {
    var html = [];

    $('#shot1 a:first').attr('href', shot.url);
    $('#shot1 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');
    
    $('#shot1 ul').html(html.join(''));
});
});

As you'll no doubt notice, this chunk of code will look pretty familiar to our old markup. The function will now perform the following steps whenever the document is ready:

  • Take the shot with the ID of '332826' from Dribbble
  • Look for an element with an id of 'Shot1'
  • Populate the link contained with the shot URL from Dribbble
  • Serve up the image location into the child <img>source
  • Create three <li> elements within the <ul>
  • Insert relevant <span> elements to create the icons and count figures.

The good news here is that because we've been careful with our class names throughout the project, we don't need to make any changes to the CSS. And with that, we've finished our first featured content item using the Envato Dribbble shot with the ID of 332826.



Step 16: Fill in the Gaps

Up to this point, we've been working with just one featured content item, although we've created some placeholder markup for an additional five featured content items. Now's the time to fill in the blanks and get this design looking like a proper interface! This process is dead easy. Simply copy the featured content item from the last step and duplicate it five times, changing the ID for each. This part of the index.html file looks like this:

<div class="three columns content-items">
                    <figure id="shot1">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                        <ul class="icon-group"></ul>
                    </figure>
                </div>

               <div class="three columns content-items">
                    <figure id="shot2">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                        <ul class="icon-group"></ul>
                    </figure>
                </div>

               <div class="three columns content-items">
                    <figure id="shot3">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                    
                         <ul class="icon-group"></ul>
                    </figure>
                </div>

                <div class="three columns content-items">
                    <figure id="shot4">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                        <ul class="icon-group"></ul>
                    </figure>
                </div>

               <div class="three columns content-items">
                    <figure id="shot5">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                    
                            <ul class="icon-group"></ul>
                    </figure>
                </div>

               <div class="three columns content-items">
                    <figure id="shot6">
                        <a href="#" class="view-more"><span class="large-icon icon-search"></span></a>
                        <img src=".">
                    
                         <ul class="icon-group"></ul>
                    </figure>
                </div>

        </div><!-- end row -->

Next, we need to create functions for each of the featured content items. In your js/jribbble.shots.js file, duplicate the function from the last step as required, changing the shot ID and the element ID for each. Our final script is as follows:

$(document).ready(function () {	

$.jribbble.getShotById(332826, function (shot) {
    var html = [];

    $('#shot1 a:first').attr('href', shot.url);
    $('#shot1 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');

    $('#shot1 ul').html(html.join(''));
});

$.jribbble.getShotById(315548, function (shot) {
    var html = [];

    $('#shot2 a:first').attr('href', shot.url);
    $('#shot2 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');
    
    $('#shot2 ul').html(html.join(''));
});

$.jribbble.getShotById(247707, function (shot) {
    var html = [];

    $('#shot3 a:first').attr('href', shot.url);
    $('#shot3 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');
    
    $('#shot3 ul').html(html.join(''));
});

$.jribbble.getShotById(201434, function (shot) {
    var html = [];

    $('#shot4 a:first').attr('href', shot.url);
    $('#shot4 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');

    $('#shot4 ul').html(html.join(''));
});

$.jribbble.getShotById(70447, function (shot) {
    var html = [];

    $('#shot5 a:first').attr('href', shot.url);
    $('#shot5 img').attr('src', shot.image_url);
    
    html.push('<li><span data-icon="&#x24;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.views_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x23;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.likes_count + '</span></span></li>');
    html.push('<li><span data-icon="&#x22;" aria-hidden="true" class="list-icon"><span class="list-number"> ' + shot.comments_count + '</span></span></li>');
    
    $('#shot5 ul').html(html.join(''));
});

$.jribbble.getShotById(722560, function (shot) {
    var html = [];

    $('#shot6 a:first').attr('href', shot.url);
    $('#shot6 img').attr('src', shot.image_url);
    
    html.push('<li><span class="list-icon icon-eye"></span><span class="list-number"> ' + shot.views_count + '</span></li>');
    html.push('<li><span class="list-icon icon-heart"></span><span class="list-number"> ' + shot.likes_count + '</span></li>');
    html.push('<li><span class="list-icon icon-comment"></span><span class="list-number"> ' + shot.comments_count + '</span></li>');
    
    $('#shot6 ul').html(html.join(''));
});
});

As a quick note, if you were really focused on reducing HTTP requests down to a bare minimum, you could easily house each of these functions in <script> tags within your HTML. I've chosen not to do this today to make the separation between the HTML and the Javascript easier to understand.

If you've followed along closely, your main content section of the site will look like this (scaled down in size):


Great work! Take a moment to grab a coffee and a break before we start on the last part of the project... turning the interface responsive.


Step 18: Plan out the Responsive Design

Since we're desigining in-browser, we don't have the benefit of referring to a mockup for small viewport display. Before we get ahead of ourselves, it's a good idea to plan out the general direction of the media queries and set up some basic responsive infrastructure.

Here are the key features our design will have at smaller viewports:

  • The content items will change from spanning three-across to two across for viewports between 480px and 767px
  • The content items will stack on top of each other for viewports <480px (i.e. mobile view).
  • The interface navigation will drop under the logo, taking up 100% of the width of the viewport when under 767px
  • Margins between items will be tweaked as required.

As you can see, this is a pretty simple game plan, and can be achieved in just two media queries.

Under the Media Queries section of your stylesheet, create the following two media queries:

@media only screen and (max-width: 767px) {
/* Indivual rules will go here */
}

@media only screen and (max-width: 480px) {
/* Indivual rules will go here */
}

Step 19: Adapt the Navigation

There's loads of ways that we could approach our navigation menu in responsive contexts, but for today we'll be keeping things quick, basic and non-javascript dependent.

In the first media query (under 767px) created in the previous step, add in the following code:

@media only screen and (max-width: 767px) {
  nav {
    width:100%;
    right:0;
    left:0;
    top: 80px;
    background-color: rgb(63, 63, 63);
    box-shadow: 0px -3px 3px rgba(0,0,0,.2) inset;
  }

  nav ul {
    position: relative;
    z-index: 999;
  }
}

This simple block of code moves the navigation under the main header, changes its width to 100% and adds some basic styling.



Step 20: Change the Layout of the Featured Content Items

Referring back to our responsive mini-plan, between 480px and 767px, the featured content items will display two-wide, and under 480px will display one-wide, stacked on top of each other.

First, we'll expand our first media query to create a two-wide featured content item display.

@media only screen and (max-width: 767px) {
  nav {...}

  nav ul {...}

.row {
    width:auto;
    min-width: 0;}

  /* Two-wide display at 50% width of row */
  .row .three {width:50%}

  figure > img {
  /* Ensure that images don't scale higher than their original size */
  max-width: 300px;
  margin: 0 auto;
  display: block;
  }
}

Next, add the following rules to the second media query to manage viewports narrower than 480px:

@media only screen and (max-width: 480px) {

  .row .three {width:100%}
}

Step 21: Tweak Margins and Spacing

Finally, all we need to do to complete our design is to tweak some of the margins to ensure that we maintain sensible margins between elements.

In our <767px media query, add the following rules:

@media only screen and (max-width: 767px) {
  nav {...}

  nav ul {...}

  .row {...}

  .row .three {...}

  figure > img {...}

  .content-items {margin-bottom: 7.5%;}

  .featured-content {margin-top: 12%;}

  a.view-more {bottom: 31px;}
}

And in our <480px media query, the following rules:

@media only screen and (max-width: 480px) {

  .row .three {...}

  .content-items {margin-bottom: 10%;}

  .featured-content {margin-top: 19%;}
}

Maintaining our margins in percentages whenever possible allows our interface to look good at practically any viewport size, not just a set of pre-determined breakpoints.


Take a moment to clean up your code, make sure that everything is ordered logically and step back and appreciate all of your hard work. We're finished!


Conclusion

If you've followed along through this entire tutorial, you'll now be armed with a range of smart techniques to boost the performance of your sites and all the tools that you'll need to encode images and icon fonts into base64 encoded data URIs.

Bear in mind that data URIs aren't always the best choice for every situation and discretion needs to be used when deciding whether encoding your site's assets is the best solution, and it's important to balance reduced HTTP requests with file size and code maintainability and readability. Used well, data URIs are an invaluable tool in your webdesign toolbox.

I hope you've enjoyed this tutorial as much as I've enjoyed bringing it to you!

How have you used data URIs in your projects? Leave a comment below.

Advertisement