Crafting a design system for higher education

As a front-end developer at a University, I can see first hand why design systems can be such an integral part of an organization’s communication (and marketing) strategy. This post aims to explain the reasons why large organizations, including higher education, can benefit from having a robust design system in place.

What process is like before a design system

At the University I work at, there are talented designers spread all over campus working for various departments. The offices and departments that are fortunate enough to have a designer working for them utilize their designer’s talents to make flyers, PDFs, and web sites. These designers all have their own style, their own tone, and their own voice. Certain departments use Arial and Helvetica for their typography on print pieces while other departments use Whitney and Sentinel.

It doesn’t take a branding expert to see why this is problematic. All parts of the communications and marketing stakeholders need to be rowing in the same direction in regards to branding.

For most other offices and departments, there are no designers on staff. This means that someone who isn’t necessarily trained in web or design is now responsible for the office’s web site or flyer design. Often times there aren’t enough tools available for this person to create a nice web site and what ends up happening in some cases is that it becomes easier to create a Word document or InDesign file and put a link to that on their site. Or content is simply copied on to the website from a Word document.

I don’t blame offices and departments around campus for doing the best they can with the limited resources they have, but there’s got to be a better way…

Hypothesis: a design system can empower existing designers and lower the barrier for non-technical web editors

It’s often easier to blame your web editors or departments for being difficult to work with or not technically capable. In higher ed, we seem to think we’re a little special. We think we have such a unique situation that no industry solutions could possibly work. Often times this leads to a web team trying to support all their institution’s web sites and all the sites look a little different and are structured a little differently. This leads to an apathetic web team who is constantly putting out fires.

To be fair, I can understand the apathy because it can seem like a dauntingly huge problem to solve. However, this doesn’t seem like a sustainable solution for the long term. There needs to be a process, a plan, in place that allows for quickly building a branded website within the organization. It needs to be so easy that an intern, administrative assistant, or anyone really whose job isn’t web. If it doesn’t work for them, the website will fail because these are the people who are updating the website.

What if there was a way to empower existing designers in the college to all be on the same page? What if we provided tools to web editors in departments that don’t have a lot of technical literacy to make their jobs as easy as possible?

 

The first step to answering this question is to get your ducks in a row and build a design system.

This is what we’re attempting to discover at work and it’s a pretty fun process! To learn more about what a design system actually is, check out my post on the matter.

After a design system is in place, developers can integrate components into whatever CMS you’re using. If the developers are any good, they can also help craft editorial experiences that empowers non-technical users to create on-brand components using your CMS.

What’s the difference between style guides, pattern libraries, and design systems?

In my organization, we’re trying to implement an integrated marketing plan which will be helped along greatly by a sound design system. In order to implement this properly, a shared vocabulary needed to be created so we could be sure we were referring to the same thing. In this post I do my best to explain the terms style guides (editorial and visual), pattern libraries, and design systems.

What a design system consists of
What a design system consists of

A design system forces your organization to think about people and process before a technology solution is introduced.

I think there is a lot of confusion over what exactly a design system is. In my handy Venn diagram, I’ve included what I think a design system consists of. Note that the size of the circles don’t correspond to importance.

Principles are your organization’s foundational ideas that your editorial style guide, visual style guide, and pattern library will live by. These are the ideas that ideally should be running through your organization and be expressed in the style guides and pattern library.

For our design system, I’ve taken this area a bit further and aim to include our main governance policies for our most strategic sites and marketing assets. Most of the principles will be abstract concepts, but some specificity certainly can’t hurt and can provide the organization with a public facing place to review web policies. This is especially important for large, decentralized organizations.

Editorial style guide is exactly what it sounds like. It’s the preferred editorial style for web writing. Including things like whether to use AM/PM or a.m./p.m. when referring to time as well as agreed upon vocabulary for the institution make an editorial style guide useful. If some sites are using “dorms” and some sites are using “residence halls” to describe on-campus housing, a standard should be picked and added to the editorial style guide. Overall voice and tone is also an important piece of an editorial style guide.

Visual style guide is what most designers would call a “branding guide” back in the day. Well crafted branding guides would typically include some editorial guidelines, including voice and tone, whereas a visual style guide only focuses on the core visual pieces of an institution. Items like logo usage, colors, color combinations, typography, and spacing are pillars of a visual style guide. These are the areas of the brand that won’t often change and should be referred to often. To use Brad Frost’s analogy of the store front/workshop, this is the store front. The pattern library is the workshop.

Pattern library is where all the components that are available live. Ideally these patterns are created using the core tenets of the visual style guide such as proper spacing, typography, and colors. A pattern library is far more dynamic than the visual style guide. The web is never finished, new needs are always being created, and therefore new patterns may need to be created to serve those needs. Pattern libraries are where designers can go for inspiration and download code samples. Pattern libraries are where the rubber meets the road in your design system.

You may notice in my Venn diagram above, that I have the visual and editorial style guides overlapping with the pattern library. It is because it’s impossible to completely separate patterns from visual style and editorial style.

Your pattern library is a place where stakeholders can see the fruits of the design system in action. If there are strong editorial and visual guidelines, a pattern library can really shine and stakeholders can see components that can be used across the organization that comply with the brand.

Using your Android phone as a pet/security camera with IP Cam and Ivideon

My wife and I just got a new puppy and are crate training him. We’d love to know how he’s doing if we’re not there so I decided to look into some DIY pet-cam solutions. Using the IP webcam app for Android and Ivideon cloud streaming service, I was quickly able to get up and running with a perfect solution that allows me to check up on my pet when I’m not there.

What you’ll need

  • An Android device that can run IP webcam (free)
  • An Ivideon account (free)

Step 1: Create Ivideon account

Make an Ivideon account. Head over to ivideon.com and sign up. Follow the steps to create your account.

Step 2: Configure IP webcam

Download the IP webcam app to your phone. Open the app and find “Connection Settings” > Cloud streaming.

It should say “INACTIVE” at the beginning (see above). Tap this and then tap “Ivideon account” to connect to Ivideon.

After you connect to your Ivideon account, the app will present you with a message that it was successful. You may think that you’re done, but you’re not! There’s one more important step.

Step 4: Start server

The final step is to enable the server on the IP webcam app which will start broadcasting your stream to Ivideon. Scroll all the way to the bottom to find this.

Once your server has started, you can login to your Ivideon account and see your camera!

Protip: Turn off your screen to prevent hard core battery drain.

Step 5: Login to Ivideon and watch your stream!

Go to invideon.com/my and select your camera. Now you can watch your stream!

 

Basic bash, node, and npm setup on Windows

I never thought this would be something I’d be writing about but it’s actually super exciting. This has been a major bane when trying to dev on my Windows machines. A lot of developer tools installation instructions walk you through some terminal commands, then the Windows people are all commenting at the bottom trying to figure out what additional steps need to be taken to make it work on Windows. This is crazy, and I’m glad Microsoft recognized this was a problem and included a bash prompt on Windows 10.

When I first installed it and launched it, I didn’t quite get it. I was in a ~ home directory but it wasn’t my Windows home directory which I kind of expected. I was in the home directory of this Ubuntu VM that has been booted to provide a bash prompt. I searched around and found that if you want to access your Windows home folder through the bash prompt, you’d have to:

cd /mnt/C/Users/<USER NAME>

Ok, now I can do an ll command there and see all my files, just as I’m used to when working with Linux and Macs. Pretty exciting!

Now, I don’t want to cd to my Windows home every time I login. To get around this, I edited my .bashrc file with an alias. The .bashrc file is in the home (~) folder of the Ubuntu VM.

alias home='cd /mnt/C/Users/jfitz/Workspace'
home

This sets an alias for home so anytime I want to go back to this directory I can just type “home”. It also executes so that when bash starts up I’m already in my Windows home directory.

Working with node and npm

I tried to install create-react-app via npm and got the following error.

npm ERR! Linux 3.4.0+
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install" "-g" "create-react-app"
npm ERR! node v7.4.0
npm ERR! npm  v4.0.5
npm ERR! path /usr/lib/node_modules
npm ERR! code EACCES
npm ERR! errno -13
npm ERR! syscall access

npm ERR! Error: EACCES: permission denied, access '/usr/lib/node_modules'
npm ERR!  { Error: EACCES: permission denied, access '/usr/lib/node_modules'
npm ERR!   errno: -13,
npm ERR!   code: 'EACCES',
npm ERR!   syscall: 'access',
npm ERR!   path: '/usr/lib/node_modules' }
npm ERR!
npm ERR! Please try running this command again as root/Administrator.

Turns out, this is a permissions issue that can be easily rectified.

sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/lib/node_modules
sudo chown -R $(whoami) /usr/bin

The future

It would be absolutely bonkers if you could launch Windows apps from the bash terminal. So say I’m in my Workspace folder, I’d be able to open up Sublime text or PHP Storm from the bash terminal. I looked into this situation and there is an issue thread on Github. This comment is pretty promising:

The project definitely has its issues, but its very promising to see Microsoft trying to make this work. I think we’re another year or two off from having a super solid *nix development environment in Windows.

Playing around with the Drupal 8 JSON API as a data service

I’m familiar with the concepts of REST but have never done anything in practice before. I decided I wanted to end that and start messing with the JSON API in Drupal 8. It was surprisingly easy to get going with because of the incredible resources out there. In this multi-part series, I’m going to explore how to use Drupal 8 as a data back-end for a React app.

JSON API Drupal Playlist by Mateu Aguiló Bosch

This video series is an excellent job of succinctly explaining how the JSON API can be used to make basic requests to Drupal.

Postman is amazing

In the series, Mateu is using a tool called Postman (free!), that makes managing REST requests really easy. This is amazing for testing out your API before you hook it up to an application.

Using Postman, for requests that require authentication, you can set up basic authentication (which uses your Drupal username and password) for easy testing purposes.

The examples below were all run with Postman. I decided it would most likely be useful to someone if I showed how I POSTed data to a content type using the JSON API.

Potential gotcha
Make sure in your request headers, you are specifying the Content-Type as application/vnd.api+json. This is according to the JSONAPI spec. Not doing so will throw a 422 Response from the server.

My app use case

I’m building a workout routine React application. The main idea is that users have Routines which contain Timed Tasks (could be yoga postures, jumping jacks, whatever). The Routines hold references to the Timed Tasks. The user can create a Routine of Timed Tasks, and then “play” through the routine, which will prompt the user to perform a workout action.

Timed Task content type

  • Title
  • Body
  • Task Setup
    • Minutes
    • Seconds
  • Task Duration
    • Minutes
    • Seconds
  • Task Rest
    • Minutes
    • Seconds

Routine content type

  • Title
  • Body
  • Timed Task entity reference

Now, lets use Postman to test some basic REST calls that I’m sure my React app will have to make.

Examples:

Local site url: http://drupal-8-3-0.dd:8083

Adding TimedTask content

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task

Body

{
   "data":{
      "type":"node--timed_task",
      "attributes":{
         "status":true,
         "title":"Half Moon right side",
         "body":{
            "value":"This pose is tough",
            "format":"basic_html"
         },
         "field_setup_minutes":{
            "value":0
         },
         "field_setup_seconds":{
            "value":30
         },
         "field_task_minutes":{
            "value":1
         },
         "field_task_seconds":{
            "value":0
         },
         "field_rest_minutes":{
            "value":0
         },
         "field_rest_seconds":{
            "value":20
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--timed_task",
    "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
    "attributes": {
      "nid": 56,
      "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
      "vid": 56,
      "langcode": "en",
      "status": true,
      "title": "Half Moon right side",
      "created": 1491878234,
      "changed": 1491878234,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491878234,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "This pose is tough",
        "format": "basic_html",
        "summary": null
      },
      "field_rest_minutes": 0,
      "field_rest_seconds": 20,
      "field_setup_minutes": 0,
      "field_setup_seconds": 30,
      "field_task_minutes": 1,
      "field_task_seconds": 0
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d1dd56a7-af44-4c01-931d-b441da7f40dc"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908/revision_uid"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task/ad4a8609-1b98-436c-9efd-fc9fe91ad908"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/timed_task"
  }
}

There’s a lot of stuff in there that we don’t need, but this piece is of interest:

"attributes": {
  "nid": 56,
  "uuid": "ad4a8609-1b98-436c-9efd-fc9fe91ad908",
  "vid": 56,
  "langcode": "en",
  "status": true,
  "title": "Half Moon right side",
  "created": 1491878234,
  "changed": 1491878234,
  "promote": false,
  "sticky": false,
  "revision_timestamp": 1491878234,
  "revision_log": null,
  "revision_translation_affected": true,
  "default_langcode": true,
  "content_translation_source": "und",
  "content_translation_outdated": false,
  "path": null,
  "body": {
    "value": "This pose is tough",
    "format": "basic_html",
    "summary": null
  },
  "field_rest_minutes": 0,
  "field_rest_seconds": 20,
  "field_setup_minutes": 0,
  "field_setup_seconds": 30,
  "field_task_minutes": 1,
  "field_task_seconds": 0
}

The core of the TimedTask is structured nicely for me to use in a front end framework like React.

Now, I want to be able to store a collection of these TimedTasks in a collection known as a Routine. A Routine will consist of many TimedTasks that can be rearranged.

I added 3 more Timed Tasks and noted their UUIDs in the response.

  • ad4a8609-1b98-436c-9efd-fc9fe91ad908
  • 390de987-11b2-47b4-a692-3c160bccab0a
  • 760a6c82-b0f1-4b2a-93d4-140061e878a9
  • 6548f256-a518-491a-8bba-d469778a3722

Now, lets add these as entity references to a Routine content type.

POST -> http://drupal-8-3-0.dd:8083/jsonapi/node/routine

Body

{
   "data":{
      "type":"node--routine",
      "attributes":{
         "langcode":"en",
         "status":true,
         "title":"My Yoga Routine :)",
         "body":{
            "value":"<p>This is my morning workout :D</p>\r\n",
            "format":"basic_html",
            "summary":""
         }
      },
      "relationships":{
         "field_timedtasks":{
            "data":[
               {
                  "type":"node--timed_task",
                  "id":"ad4a8609-1b98-436c-9efd-fc9fe91ad908"
               },
               {
                  "type":"node--timed_task",
                  "id":"390de987-11b2-47b4-a692-3c160bccab0a"
               },
               {
                  "type":"node--timed_task",
                  "id":"760a6c82-b0f1-4b2a-93d4-140061e878a9"
               },
               {
                  "type":"node--timed_task",
                  "id":"6548f256-a518-491a-8bba-d469778a3722"
               }
            ]
         }
      }
   }
}

Response

201 Created

{
  "data": {
    "type": "node--routine",
    "id": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
    "attributes": {
      "nid": 62,
      "uuid": "5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1",
      "vid": 62,
      "langcode": "en",
      "status": true,
      "title": "My Yoga Routine :)",
      "created": 1491879173,
      "changed": 1491879173,
      "promote": false,
      "sticky": false,
      "revision_timestamp": 1491879173,
      "revision_log": null,
      "revision_translation_affected": true,
      "default_langcode": true,
      "content_translation_source": "und",
      "content_translation_outdated": false,
      "path": null,
      "body": {
        "value": "<p>This is my morning workout :D</p>\r\n",
        "format": "basic_html",
        "summary": ""
      }
    },
    "relationships": {
      "type": {
        "data": {
          "type": "node_type--node_type",
          "id": "d50564ec-fb5e-4031-866a-38e8ca98dc0c"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/type",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/type"
        }
      },
      "uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/uid"
        }
      },
      "revision_uid": {
        "data": {
          "type": "user--user",
          "id": "a552daf1-9feb-47cf-958a-a50329b7d689"
        },
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/revision_uid",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/revision_uid"
        }
      },
      "field_timedtasks": {
        "data": [
          {
            "type": "node--timed_task",
            "id": "ad4a8609-1b98-436c-9efd-fc9fe91ad908"
          },
          {
            "type": "node--timed_task",
            "id": "390de987-11b2-47b4-a692-3c160bccab0a"
          },
          {
            "type": "node--timed_task",
            "id": "760a6c82-b0f1-4b2a-93d4-140061e878a9"
          },
          {
            "type": "node--timed_task",
            "id": "6548f256-a518-491a-8bba-d469778a3722"
          }
        ],
        "links": {
          "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/relationships/field_timedtasks",
          "related": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1/field_timedtasks"
        }
      }
    },
    "links": {
      "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine/5075e4b7-e2d6-49d0-ba65-c0b4b48fc8f1"
    }
  },
  "links": {
    "self": "http://drupal-8-3-0.dd:8083/jsonapi/node/routine"
  }
}

Using Postman, I helped validate the basic API structure that my React app will use. Next, I’m going to look into how we can hook this up into a React application.

Starting a Vimeo embed at a given timestamp using the JS API

Vimeo by far provides the best hosted video embed service out there by far, in my opinion. This post explains how to change the behavior of a Vimeo embed to skip to a given timestamp when the user plays the video. We’ll use data attributes to store the start time in the HTML.

Why would I want this?

In my situation, the college I work at runs a robust news site that often times includes videos. In that context, we format our videos to include an opening branding screen, then a brief intro paragraph to orient people to the video they are about to watch. This works great for the news site, but what if we come across a situation where we want to skip that intro and go right into the video?

Vimeo Player JavaScript API to the rescue

The Vimeo Player JS API allows developers to easily interact with their Vimeo embeds. The directions on including it are pretty straight forward, they even offer a CDN hosted version to include:

<iframe src="https://player.vimeo.com/video/208129791" width="500" height="281" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>

Next, we look for our Vimeo Player in our JavaScript. In my example, our Vimeo videos are put into our site as follows:


<div class="video" data-start-time="5">
  <iframe src="https://player.vimeo.com/video/204427616?color=498957&title=0&byline=0&portrait=0" width="640" height="360" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>
</div>

So, to access this Vimeo embed, in my JavaScript file I do something like:

if ($('div.video').length) {
  
  // Loop through all the videos
  $('div.video').each(function( index, value ) {

    // Grab the Vimeo player already on the page
    var vPlayer = new Vimeo.Player($(this).find('iframe')[0]);

    // Set a flag to determine if the player has been paused yet
    var pause = false;

    vPlayer.on('play', function() {

      // If the player has not yet been paused, start it at
      // the specified time indicated in data-start-time
      if (!pause)
        vPlayer.setCurrentTime($(value).attr('data-start-time'));
      });

    vPlayer.on('pause', function() {
      // Set the pause flag to true. Setting this prevents
      // the player from starting the video at 
      // the 'data-start-time' instead of just 
      // continuing the video where it was paused.
      pause = true;
    });
  });
}

Limitations

There’s nothing to stop people from rewinding back to the beginning and watching the intro if they choose, so if you’re trying to use this method to cut some video, it won’t work.

This also assumes you have the means to add data attributes to your HTML to store your start times. If you don’t have access to your HTML, you might need to rethink your approach.


Worked perfectly for my needs, hope it helps!

Embedding Drupal Views with the Paragraphs module

The Paragraphs module has been super helpful to developers and site builders who want to provide content editors with a solution to create amazing looking sites without having to know HTML.

I’m currently using Panels/Panelizer and Fieldable Panels Panes in the Drupal build at work to allow content editors to create the pages they want. Panels overall is great but I’ve come across situations where the In-Place Editor is buggy and/or creates confusion for users. The editing experience is a little wonky for them because between the node edit form and the In-Place Editor, there’s too many ways to skin a cat.

So, with our new department sites roll-out, I’m trying something new and moving away from Panels and going all in with Paragraphs. An immediate roadblock I ran into was How do I embed a View in my paragraphs page?

Viewreference module + Paragraphs

After asking around in Drupal IRC, a nice fellow pointed me in the direction of Viewreference. Once I came across this module, things started falling into place.

At a glance
  1. Install module
  2. Create a Paragraph bundle called “Embed view”
  3. Add a Viewreference field to the bundle
  4. Viola! Embed your views.

Install module

drush en viewreference -y

or download, unpack and upload to your module’s folder.

Create a paragraph bundle called “Embed view”

Add a view_reference field to the bundle


Now you can embed Views within a Paragraph bundle!

The result:

2016 year end review

This is becoming one of my favorite things to do around the time of each new year. It’s a great opportunity to look back at the past year and take inventory of what has been done and reflect on what could’ve been better.

2016 was the year of some Drupal deep-diving. I learned a lot about some Drupal 7 modules including Panels, Paragraphs, and especially Features. Since I manage a 140 site multisite at work, being an expert at Features is absolutely critical to consistently deploy functionality across those sites.

Launched my first Drupal 8 site at Oswego (The Campaign for SUNY Oswego) to much fanfare.

I went to Acquia Engage again in Boston this past year and met some really nice people from MediaCurrent.

I worked with a very talented art professor at the college and implemented a WordPress theme based on a class design for our local children’s museum.

2016 is the year I stepped up my freelance work and have considered starting a side-business out of it. I don’t have kids so I have free time in the evenings. I love this work as well, so why not?

I didn’t dig into front-end frameworks as much as I wanted to this year, but I still think that’s coming.

Outside of technology, my wife and I bought our first house which made the last quarter of the year fly by.

I’m looking forward to a great 2017!

Follow up on scalable, maintainable CSS

I wrote down some ramblings about this subject a while back after first reading about it through @mrmrs on Twitter. The idea of single function CSS classes was weird and seemed unnatural at first, but I was very intrigued. I considered this idea around the same time I was realizing that, at work, I basically keep creating variations of the same designs over and over. Thus bloating our CSS file and wasting time developing components that were pretty much previously completed. Then a lightbulb went off. 💡

We were in the middle of a project at work and I decided to try something. I’d create a bunch of “helper” classes that would perform single functions on an HTML element. For instance, .text-center would align the text in the center of the element. .container-800 would create a container that was 800px wide. I created about 20 or 30 of these classes and tried to come up with rules that I routinely use when creating BEM or SMACSS style components.

The result? Incredibly fast iteration! I could piece together these classes in HTML to make interfaces in our CMS so quickly that I couldn’t deny there is some real value in this.

Another result: people who work on my team can no go forth and build awesome interfaces without necessarily needing to know about SCSS, preprocessors, etc. It lowers the barrier for entry and helps groups work together faster.

It makes designer and developers speak the same language. “I think there should be a little more padding on this container” can now be remedied by the designer or the developer by modifying the padding class on the element. If the designer doesn’t know CSS or any of the tooling the developer uses, it is still possible for the designer to change the interface to their liking. This is so powerful.

If you’d like to check out a library that has created tons of these “helper” classes already, look into Tachyons. It’s a wonderful concept and library.

Successful web projects begin with content

If I had a nickel for every time someone jumps to design and layout in a web project meeting, I’d have about $5 (that’s a lot of nickels).

As Mike Monteiro puts it in his (excellent) book You’re My Favorite Client:

No one comes to a site because of the design. They come for the content or the service, like booking air travel. The design should encourage them to stay, offering a wonderfully easy-to-understand and even delightful way to interact with that content or service. Bad design can certainly bury good content, but you can’t design a “premium experience” and pour crap content into it with any expectation of success.

I think this is a little scary for people who design and make websites, because it demotes the importance of how a site looks.

Consider this post from SimpleBits:

This is my favorite website. I visit it almost every day. It’s not responsive. It’s not optimized for iPhone. It looks blurry on a Retina display. It doesn’t use the latest HTML5/CSS3 framework. It doesn’t have a thoughtful vertical rhythm. The fonts are nothing special. It is neither skeumorphic nor flat. It doesn’t have its own favicon. It doesn’t have a native app or Twitter or Instagram. It doesn’t use AJAX or SCRUM or node.js or Sinatra. It doesn’t have an API or an RSS feed or VC funding. It hasn’t been featured on a prominent tech blog or won an award.

It tells me the soups of the day.

Freely distributed information that’s relevant to the person reading it. That’s web design.

I’ve gotten incredibly invested in projects before and become so enamored with how I implemented a certain design pattern or how clever a design element was, only to see it met with a tepid response from users. This is typically what happen when you put the cart before the horse. The design could be incredible but if it’s not useful to the user, who cares? The user doesn’t look at your website the same way the person who made it does.

The user is looking for helpful content or a helpful service. They’re not there to look at how it was implemented or how it looks. The only people who do that are other designers and developers.

Most successful web projects start with great content. Design serves to highlight the content. So if all this is true, why do so many meetings quickly move into design?

This is something Monteiro also touches on repeatedly in his books, but we — people who design and create websites — all share some guilt for allowing this to happen. The people who move the conversation towards design before a) purpose of the site is settled and b) engaging content is developed simply don’t know that they’re trying to derail the process.

It’s our job to remind them what is important. It’s our job to prioritize content over design. Ideally, a great content strategist can work with the client and help tease out what is most important to be shown and developers can focus on building a great technical foundation.

So everyone who is reading this, go forth into your next meeting and remind people of the solid foundation that web projects should be built on: a purpose, something useful to the user, and great content to support it. Once those things are decided, the design will follow.