CSS at Opengov

I originally wrote this article in April of 2015. Since then, I’ve left OpenGov, but I think that the work I document in this article continues to be relevant today.

Its great to see how development teams deal with CSS. I can’t get enough of these write-ups. Seeing the similarities, new ideas, and ambition thrown out for all to consume is pretty great. In that spirit, this post takes a close look at how I approached CSS as a designer and developer at OpenGov.

No one writes articles titled “how we do Ruby on Rails at …” Developers working in real languages and their frameworks have histories, conventions, and hard limits. CSS is deceptively simple. One of the tricks is sticking to your own standards, and aim to limit what’s possible with CSS.

Where we were and how we got there

OpenGov’s transparency product has been around since 2012. Originally, it was a Rails application built with Zurb’s Foundation. Since then, the product has been redesigned a few times and the development stack has been completely overhauled. Our CSS continued to grow and grow.

Today, our applications are built with ReactJS on the front end, which talks to an API managed by Rails. We’ve replaced Foundation with our own, custom-build CSS pattern library named Ovid, after the Roman poet.

Ovid is a stand-alone private npm package that is included as a dependency in our applications. Each application imports Ovid in its own SCSS manifest, which allows the application to extend basic styles and access shared mixins, variables, and functions.

Why’d we build our own pattern library rather than using Bootstrap or sticking with Foundation? It’s mainly about flexibility, efficiency, and control. The common frameworks aim to be all things to all people. Ovid is purpose built for us, and aims to contain only what we need for our apps.

Architecture

We use Sass written in the SCSS syntax and compiled with LibSass. LibSass finally has enough feature parity with Ruby Sass to be a real option for us, and it compiles super fast. The last time I evaluated the difference, Ruby Sass compiled in 1.2s with LibSass coming in at 81ms. Over the course of the day, given the number of times I re-compile, this represents a significant speed boost.

For the moment, we use Node-Bourbon (a LibSass-port of Thoughtbot’s Bourbon mixin library) for prefixing. We’ve considered moving to Autoprefixer because it probably will run faster and allow us more flexibility and control over prefixing, but Bourbon’s mixins are helpful at consolidating repetitive CSS (my favorite is the position sugar).

There’s no grand unifying theory for the directory structure of our SCSS partials. The manifest file determines the cascade order, so who cares what directory you clump your partials in? All the same, we aim to make logical groupings of partials in our directory structure, such as helpers, forms, navigation, and buttons. In general, each partial describes one UI component.

Sass that doesn’t compile (shared variables, functions, mixins, and silent placeholder classes) are grouped together into a library partial. The 48 other base and components partials are grouped within the main Ovide manifest. In each application using Ovid, these two partials are imported, preceded by other Sass dependencies (managed by private npm).

Ovid compiles a CSS file of the framework just in case one of the apps consuming it don’t use Sass. At the present, our Rails applications import this file via Bower for inclusion in the asset pipeline.

Naming things

We use a BEM-like syntax for defining HTML class names, and try keep CSS specificity to a minimum by providing a class for all elements. That said, we’re not dogmatic about anything, so you’ll find plenty of bare HTML elements nested within class names.

Because Ovid was introduced into an existing SCSS ecosystem, we decided to prefix all Ovid component classes with “ui-”. While this may break my keyboard’s u and i keys from over-use, the namespacing helps encapsulate Ovid: components can only inherit styles from an application’s stylesheet, never the other way around.

Variable names are abstract and written in camelCase. For families of variables, we append a number to the base name with 1 being the biggest or darkest. This fits in with the h1, h2, etc. convention.

$colorGray1: #444; 
$colorGray2: #737373;
$colorGray3: #909090;
$colorGray4: #ccc;
$colorGray5: #eaeaea;
$colorGray6: #f7f7f7;

Ovid factoids

  • Total rules: 985
  • Total selectors: 1754
  • Average selectors per rule: 1.8
  • File size: 118.28 KB
  • Gzip size: 17.34 KB
  • We roll our own font icon set, called OpenGovicons (of course). As of this writing, there are 135 glyphs and are due for a pruning. Several special icons are actually composed of two glyphs with transparent color for a two-toned effect. We use Fontastic to manage OpenGovicons.
  • For a long time we were using reset.css to wipe our slate clean. We’ve switched to normalize.css, because it gives us a level playing field rather than blowing the whole thing away.
  • Reusable ReactJS components are distributed along with the SCSS.

Living styleguide with KSS

The best thing about developing with Ovid is our living styleguide. We use Knyle Style Sheets (KSS) to compile a styleguide from formatted comments in Ovid’s Sass partials. For example, we can generate documentation for our button family like this:

// Buttons 
//
// Use `.ui-button` on any element to create a styled button,
// or use the `<button>` element (with the HTML class).
// For primary actions, use `.ui-button.primary`.
//
// Markup:
// <a class=”ui-button {$modifiers}”>Click me</a>
//
// :hover — Hover state
// :active — Active state
// :disabled — Disabled
// :focus — Focus
// .primary — Primary
// .primary:hover — Primary hover state
// .primary:active — Primary active state
// .primary:disabled — Primary disabled state
// .ghost — Starts looking like a link, becomes a button on hover
// .small — Small
//
// Styleguide 6.1
.ui-button {…}

We have a grunt serve task that watches SCSS files for changes, recompiles the stylesheets, builds the styleguide (with kss-node), and runs a local development server. Since you can define markup for a component within the SCSS partial, we can create components in isolation without having to spin up our applications locally.

We deploy the styleguide via Github pages, because it’s just so convenient.

Build and deployment

Linting is an important part of developing SCSS at OpenGov. We use the wonderful scss-lint gem, which allows us to specify our SCSS preferences such as alphabetical ordering of properties, and no nesting beyond 3 levels. SCSS and JS linting are done together at the command line or automatically.

Most of our other build processes are grunt tasks. We have tasks for local development, building a distribution, and running the test suite on our shared React components. Thanks to the wonderful CSSmetrics plugin, we keep an eye on Ovid’s selector count (we support IE8) and compressed size.

Refactoring

Everyone’s a big fan of writing code that is slimmer, more encapsulated within a partial, and understandable. There’s no better feeling than seeing that red number in your PR larger than the green one. I tend to do pragmatic refactors, ones that correspond to new application features that require substantial new or expanded components from Ovid. This is great because refactoring can be accounted for within sprint planning estimates.

That’s it

Hopefully this is useful. If you have questions or comments about anything here, please feel free to Tweet them to me @andrewliebchen!

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.