Welcome to our Flatiron Engineering blog series. In this series, the engineering team behind Flatiron’s learning platform pull back the curtain and share insights into how we build our products.
A few days ago, I walked you through the first steps Flatiron School's engineering team went through to overhaul the CSS in our Learn.co codebase. We covered how to take a visual inventory of a site and translate repeating patterns into CSS. Today, we'll take a look at the next few steps in the process. Be sure to read Part 1 of this post before reading this.
STEP 3: REWRITE
We’ve designed our system. Now it’s time to rewrite all of our markup. We’ll look at one of the most commonly-used patterns as an example:
.media-block has three main parts: the parent
.media-block class and two child classes:
When writing the markup, start by outlining the basic container and objects. Rely on the semantic BEM syntax to provide information the the relationships between elements - parent <> child, nesting level, etc.
1 2 3 4 5 6 7 8 9 10 11
<div class='media-block'> <div class='media-block__media'> <!-- circle with user’s profile picture --> </div> <div class='media-block__content'> <!-- text object: user name --> <!-- text object: time ago --> <!-- text object: action completed by user --> </div> </div>
After you’ve built the frame, add text and media elements to fill out the card. Your end product is a fully encapsulated, reusable block of markup that’s ready to drop into any view.
1 2 3 4 5 6 7 8 9 10 11 12
<div class='media-block' <div class='media-blockmedia' <div class='image-frame image-frame--border-radius-full' <img class='image-frameimage' src='img.png'/> </div> </div> <div class='media-block__content'> <span class='heading heading--level-5'>Brent...</span> <span class='heading heading--level-6'>5 min ago</span> <h5 class='heading heading--level-5'>Built...</h5> </div> </div>
.js--. For example,
This was also a good opportunity to decouple our JS and CSS. Moving forward,
.js-- classes have no styles attached to them. They’re reserved for JS selectors only.
STEP 4: ONBOARD THE TEAM
We onboarded our team in three phases.
In phase 1, we shipped new stylesheets and markup as dark code, “hidden” behind a feature flag. I also held a brief training session with the team on BEM syntax and OOCSS basics.
In phase 2, we QA’d the feature while gradually turning the feature flag on for a greater and greater percentage of our users. I held a second training session with the team where we did some hands-on practice rewriting existing markup.
In phase 3, we flipped the feature live for 100% of our users. I shipped a style guide with full framework documentation, and then paired with teammates as needed on new feature builds, until the whole team was comfortable with the new system. In all, the process took about two and half months, with three dedicated devs at peak, eventually scaling back down to one.
So what did all this work earn us? First, we saw impressive UX gains. Paring down our code gave us faster pageloads, a more consistent UI, and greater parity with our product mocks. We also enjoyed some nice developer gains. This plug-and-play system allows our team to ship faster. Now we can build new views without writing any new CSS. We can reuse markup with predictable results, so prototyping becomes faster and better reflects the final product, and devs don’t need much front-end experience to be able to put up a nice looking view quickly. Maybe most impressive our all were our performance gains. Below are unzipped, unminified size comparisons for our before-and-after assets payload. You’ll see we were able to reduce our payload by 10X. Here’s average page render times for our most heavily trafficked views. We cut most of these in half. We devoted a lot of resources to this project, but as you can see from these big wins, it was well worth it. The biggest lesson learned was not to wait. You can save yourself a lot of pain by applying OO design to your CSS from the start.
Nicole Sullivan’s OOCSS
SMACSS Style Guide
This post initially appeared on Kate's blog on February 20, 2017.