@mdo

Powered by Fusion

GitHub's CSS

I’m always interested in the development details of other products, particularly their styleguides and approach to CSS. Given my penchant for the otherwise inane CSS details, I decided to write a bit about GitHub’s CSS.

Contents

Given the length, here’s a helpful table of contents.

Quick facts

A survey of our current state of CSS shows:

  • The preprocessor of choice is SCSS.
  • We have well over 100 individual source stylesheets that we compile before serving it up in production.
  • That compiled CSS is served via two separate stylesheets to avoid the IE<10 selector limit.
  • Those two stylesheets weigh in at around 90kb total.
  • There’s no particular “CSS architecture” in place.
  • Pixels are the unit of choice, although we do have some ems lying around.
  • Added 7/24/14: We use Normalize.css and a mix of our own reset styles.

Preprocessor

As mentioned above, we use SCSS. This was a choice made much before my time and I’m a-okay with it (despite Bootstrap currently being written in LESS). Our SCSS is compiled by the Rails asset pipeline with some help from Sprockets (for the time being) for including the files. Read about that a bit farther down.

What about LESS, or Stylus, or …? I don’t think LESS was ever a choice for GitHub, but I can’t really speak to that. We probably wouldn’t switch at this point either as there is no clear benefit.

Why a preprocessor? Our internal framework includes a relatively small set of variables (like font stacks and brand colors) and mixins (mostly for vendor prefixed properties) that make writing code faster and easier.

We don’t currently use Autoprefixer, but we really should as it would basically negate all our existing mixins. Hopefully we’ll implement it soon.

We also don’t currently use source maps, but that’ll be changing soon. (If you don’t know, source maps allow you to see in the Inspector what source SCSS file a particular set of styles are coming from rather than a compiled and minified stylesheet. They’re awesome.)

Added 7/24/14: Other than that, our use of the tools SCSS provides is quite limited. We use color variables, mixins, color functions, math functions, and nesting. We don’t need to iterate over classes, set global options, or anything else really. I enjoy the simplicity this affords.

Architecture

Common CSS architectures include BEM and OOCSS. We lean towards OOCSS, but we have no holistic approach. We do tend to write all our new stuff with a nebulous but basic approach:

  • Aim for classes over everything else in selectors
  • Avoid unnecessary nesting
  • Use (single) dashes in class names
  • Keep things as short as possible without adding confusion

I’ll write more about my preferred CSS architecture in another post. For now, the above sums up GitHub’s approach, which while not perfect, serves its purpose well enough.

Linting

We didn’t start linting our SCSS until several weeks ago. We had common enough conventions, but everyone’s style and formatting was somewhat unique. Today, every CI build includes basic SCSS linting and will fail if:

  • You have a class in your CSS but not anywhere in your app/views/ templates.
  • You are using the same selector multiple times (as they should almost always be combined).
  • The general formatting rules (nesting limits, line breaks between rulesets, lack of space after :s, etc) are broken.

All in all, these few rules keep our stuff decently tidy. It doesn’t account for discrepancies in commenting styles or general architecture, but that’s the stuff teams need to address themselves with real documentation. It’s something everyone here is open to improving and iterating on.

Two bundles

GitHub.com has two CSS bundles, github and github2. The split was added years ago to solve the problem of Internet Explorer’s 4,095 selectors per file limit. This applies to IE9 and down, and since GitHub requires a minimum of IE9, our split bundle approach must stay.

This is because, as of today, GitHub has around 7,000 selectors across those two files. How’s that compare to other sites?

  • Bootstrap v3.2.0 has just under 1,900 selectors
  • Twitter has just under 8,900 selectors
  • NY Times has just under 2,100 selectors
  • SoundCloud has under 1,100 selectors (Edit: Previously I reported this at 7,400, which is the old SoundCloud)

These numbers were gathered using cssstats.com. It’s an awesome little tool that looks at your CSS in ways most folks, including myself, usually don’t. We also have graphs for this internally at GitHub and usually use those for our own purposes.

Included via Sprockets

GitHub’s CSS, and JavaScript, is bundled via Sprockets and require. We maintain our two CSS bundles with separate subdirectories within the app/assets/stylesheets directory. Here’s how that looks:

/*
 = require primer/basecoat/normalize
 = require primer/basecoat/base
 = require primer/basecoat/forms
 = require primer/basecoat/type
 = require primer/basecoat/utility
 = require_directory ./shared
 = require_directory ./_plugins
 = require_directory ./graphs
 = require primer-user-content/components/markdown
 = require primer-user-content/components/syntax-pygments
 = require primer/components/buttons
 = require primer/components/navigation
 = require primer/components/behavior
 = require primer/components/alerts
 = require primer/components/tooltips
 = require primer/components/counter
 = require primer-select-menu
 = require octicons

 = require_directory .
*/

We include our dependencies—Primer is our internal framework—and then load the entire directory’s SCSS files in whatever order Sprockets decides to include them (I believe it’s alphabetical). The ease in which we can bundle our stylesheets—by simply writing require_directory .—is awesome, but it also kind of sucks.

The order in which styles get applied matters. It really shouldn’t, but in every design system there are rules and a basic hierarchy of styling. With the Sprockets approach, we sometimes run into specificity problems. This happens because new files can be added to either bundle at any time. Depending on their file name, they appear in different spots in the compiled CSS.

Additionally, using Sprockets means that your SCSS files don’t have immediate and automatic access to your global variables and mixins. That means you have to @import them explicitly at the top of any file that references a variable or mixin.

Given the repetition and occasional specificity headache, we have a pull request open to switch to explicit @imports. The added benefit is clearer insight into our CSS and easier willy-nilly experimentation with our aforementioned bundles. The downside is having to maintain that list, but I like to think of this as an added control to the overall system.

Performance

Added 7/24/14: Bundle sizes and selector count graphs.

Internally we use tons of graphs to monitor how the site and API are doing. We also track a few interesting frontend stats. For example, here’s the size of our two CSS bundles for the last three months:

GitHub bundle sizes

Similarly, here’s the number of selectors over the last month on our blob (or file) pages. Clearly we still have some work to do there to get those element tag selectors down.

GitHub selector count

Because we regularly deploy updated CSS, and we deploy dozens of times each day, we’re constantly busting the caches on our decently large CSS files. We haven’t done much in the way of optimizing these file sizes or limiting the cache busting, but we are starting to look into that more closely. It’d be awesome to have a core bundle that hopefully changes very infrequently and secondary bundle that can be more volatile.

Speaking of which, at Twitter we had (I’m unsure if they still have this) two bundles, core and more. core was all the styles needed to keep the time to first tweet number as low as possible. Everything else was in more. Given GitHub’s love of all things fast—here, it’s not shipped until it’s fast—it’s something we plan on looking into. Currently our bundle split is arbitrary.

Generally, selector performance isn’t something we concern ourselves with. We’re aware of bad practices—over-nesting, IDs, elements, etc—but we don’t try to over optimize. The one exception has been diff pages. Due to the extensive markup required in rendering a diff, we avoid attribute selectors like [class^="octicon"]. When used too often, those attribute selectors can (and have) crashed browsers.

For the curious, Jon Rohan, a fellow GitHub design-gineer, has a great talk about GitHub’s CSS performance that covers these problems.

Documentation

GitHub styleguide

Speaking of which, we do an okay job of this, but we’re working on making improvements. We have a publicly accessible CSS styleguide and all our general rules for writing CSS live there, as well as examples of most components. It’s built on KSS, a styleguide generator of sorts, and lives right in the github/github code base.

It’s not perfect, but it helps folks find and use things. It’s also a great way to show new folks how we do things to get them up to speed faster (like me when I joined nearly two years ago).

Primer

Primer

I alluded to it earlier, but for folks who don’t know, Primer is our internal framework for common styles and components across our public and internal apps. It includes:

  • Normalize
  • Global styles for box-sizing, typography, links, etc
  • Navigation
  • Forms
  • Grid system
  • Markdown styles
  • Custom select menu

We use it on GitHub.com, Gist, and several internal apps. As a rule of thumb, if it can or should be used in another app, we consider putting it in Primer.

Most of Primer’s components are documented in our styleguide (e.g., navigation, tooltips, etc). However, we’ve recently been going to town on updating and improving Primer, so lots of things are in flux there. We’ll be updating that soon to include all the things.

And for those about to ask, I’d love to open source parts of Primer, but there’s not much happening on that front in the near future. I’m hopeful though.

Refactoring

We have our fair share of legacy code at GitHub, and that includes our CSS. Unlike a public open source project with strict versioning rules, we burn shit down regularly if it suites us. Finding things to remove tends to happen one of two ways:

  • Manually finding things that look alike but actually have different HTML and CSS, then combining them.
  • Running a script that greps for a class in our CSS to see if it exists in our views. (We’ve recently made this part of our CI tests, so we constantly see it now.)

The actual process of refactoring CSS is probably not unique to GitHub. We find shit to remove, burn it down, open a PR, ping the CSS team, and ship it as fast as we can. As to who removes code, anyone can do it. We have tons of great folks adding to GitHub, but we also have just as many nerds looking at what we can remove.

Questions?

Have a question about how we do things at GitHub, or anything else? Ask away on Twitter or my feedback repo.

GitHub.com flow

The term GitHub flow refers to how one can develop software with GitHub. It’s a process that minimizes friction, enables asynchronous communication (and development), and is decently flexible. For an overview, read Understanding the GitHub Flow on GitHub Guides.

Applying the GitHub flow to development of GitHub.com The Website™ builds on that, and tends to go something like this.

1. Find something to work on

Issues on the github/github repository are the easiest way to find things to do. Anyone can open them, and anyone can comment on them. User and team at mentions are available for pinging folks with bugs, feedback, or questions.

It’s worth mentioning though that we sometimes jump right into code from a meatspace discussion or from a tweet. Twitter is the best way to find out if we have bugs or performance problems in real-time while deploying code.

Bottom line? Get to writing code as soon as possible.

2. Branching

At GitHub, almost every new contribution happens in a branch. What’s in master should always be stable, passing tests, and ready to deploy in an instant. When I figure out what I’m working on, I cut a branch from master and get to work.

My branch naming scheme is sometimes serious and relevant, and usually not useful in the slightest. sidebar_hit_areas and today_we_celebrate_our_independence_day are both branch names that I’ve recently worked on. Both are long gone though.

Great branches really should include accurate names, but most of the time it doesn’t matter because they’re so short lived. Plus, snagging the name of a branch is as easy as copy-pasting from the top of a pull request.

Bottom line? Just cut a branch and write some code.

Not every change happens in a branch. Super small changes can be made right to master should the occasion, or emergency, call for it.

3. Pull requests

Branches are ephemeral. They exist for the sole purpose of creating a pull request. Pull requests are more important than branches for a number of reasons:

  • Suggested changes are (justifiably so) more likely to be shipped if there’s an available pull request for it.
  • Branches get deleted, and thus code can be lost. Pull requests are never deleted, only closed or merged.
  • Pull requests, like issues, are search-able, label-able, milestone-able, and mention-able. Branches? Not so much.
  • Pull requests can be discussed in the open and linked to.

Pull requests should be created as soon as possible. You should push your code and open a pull request when:

  • You have changes that are completely finished and just need review before merging.
  • You have a bunch of things to do and need to use GitHub task lists to outline what’s left.
  • You have questions and have no idea how to finish a part of your intended changes without pinging coworkers.
  • You have an idea that you’d like to discuss and test, but might not ever intend to ship.

As a side note, the GitHub.com code base has 50% more pull requests than issues:

Bottom line? Pull requests are cheap and effective. Create them early and often.

4. Discuss your code

You can define a pull request a few different ways, but first and foremost it’s a discussion around code. Code review. The best code reviews (and thus, pull requests):

  • Have a limited set of changes
  • Specifically at mention individuals or teams
  • Include before and after screenshots, if dealing with visible front-end changes

There’s no harm in creating lots of pull requests for discussion. If you’re concerned about pushing unfinished work, let people know you’re not yet ready for feedback or merging to master. Two easy ways are to preface your pull request title with WIP: or to include a row of :construction: emoji in the opening comment.

Bottom line? Make it as easy as possible for your peers to help you ship your code.

5. Shipping it

When it comes to deploying or shipping your code for GitHub.com, you have a few options:

  • Branch deploy: deploying your branch temporarily to production in place of master.
  • Branch lab deploy: deploying your branch to a specific server, which when deployed, gives you a unique URL (e.g., hi_mom.branch.github.com).
  • Merging to master: merge your branch via the merge form on your pull request. Your branch is merged, tests are run, and master is redeployed with your changes to production.

The best thing to do is use branch lab because you get a real URL you can share with folks around the office. URLs are the best way to drop a link into Campfire to invite folks to view some changes. Also, there’s basically no limit to how many branches can be deployed on branch lab.

When you’re changes have shipped—meaning, they’ve been merged to master—you delete the branch. GitHub will automatically prompt you do to so after merging to ensure your list of branches stays lean.

Bottom line? When you’re ready to ship, test your changes in an isolated branch lab, then merge to master.


Shoutout to Hubot

Hubot is our Campfire bot that literally does (almost) all the things. Find Google images, remember things, run tests, and most importantly, deploy code. When I’m ready to deploy code, I just pop into Campfire and tell Hubot to get to it:

bash /deploy github/hi_mom to branch-lab

Hubot will automerge master if there are new changes not in my branch yet, run tests again, and then deploy my branch. If my tests fail or haven’t yet finished running, Hubot tells me that right in Campfire.


More reading

If any of this interests you, definitely check out some other articles from fellow GitHubbers:


Questions?

This post started from a long Twitter conversation the other day. Have a question about how we do things at GitHub? Ask away on Twitter or my feedback repo.

Dream on

This morning I was mentioned in a brief Twitter conversation, and because Jeffrey is the best, I feel obliged to reply.

I encourage you to read the mentioned post by Elliot Jay Stocks. In it Elliot asks the question, Why have today’s designers stopped dreaming? Between the article and the tweets, there are two topics I’d like to comment on.

Tools

First, Brendan blames—among other unnamed things—frameworks. Consider my interest piqued.

While Elliot directs his post towards the craftsmen—those swinging the tools—Brendan throws the tools themselves under the bus. He’s not alone in that line of thinking. Lots of folks hate on today’s frameworks, design tools, and the like. Usually that dislike comes out of personal preference or limited experience. That doesn’t invalidate anyone else’s use of those tools though, and we’ll all do well to remember that.

Let’s consider another “framework” for a moment—iOS. Is the most important operating system of the last decade to blame for a stagnation in app design and creativity? Some might say so, but have you seen the amazing apps that are being built on iOS? They’re fucking astounding!

Are frameworks the same as a mobile operating system? No, but they serve a familiar role—abstraction of common aesthetics, behaviors, and components with a focus on documentation. As absurd as it might sound, it’s hard not to see at least some similarity between the two.

Are these frameworks and other tools impervious to criticism? Definitely not. (Have you seen the number of issues against Bootstrap? It’s over 13,000!) However, it’s important to acknowledge that amazing things can be done with just about any tool if you have the right mindset.

Bottom line, the tools aren’t (completely) to blame.

Web workers

Second, from the article:

The web right now is a beautiful place. Web design has matured as an industry and the technology now enables us to create whatever we might dream of in HTML, CSS and JavaScript. But it’s clear that laziness amongst designers has never been more rife.

Thousands and thousands of cookie-cutter sites litter the internet, each one drawing just a little too much influence from the last. The blame is not to be laid at the feet of sites that provide actual website templates, but at the feet of professional designers who are churning out ‘bespoke’ sites that might as well be templates.

I dare say what we’re witnessing, and what Elliot and Brendan are referring to, is not laziness. The web design and development community is in a completely different place from when we got started.

We’ve been doing this stuff for 10, 15, or more years now. Those lazy designers you’re seeing? They’re lawyers, stay-at-home dads, bored teenagers, interior decorators, anthropology majors, teachers, and more. And they all just learned HTML and CSS last Tuesday afternoon while we were all eating our burritos and playing video games at work. (Hi, that’s me in case you didn’t realize.)

Today’s web is filled with millions of people who don’t make websites professionally. They make them simply because they want to and the tools are in place so that they can. That’s fucking awesome!

This doesn’t account for everyone though. Are some designers lazy? Yup, and at times I’m definitely one of them. The site Elliot described? It might as well be any number of the example templates that come with Bootstrap. And I’m perfectly okay with that.

Right or wrong

Elliot and Brendan aren’t wrong. There are lazy designers and developers out there, and tools and trends can make it easier for folks to be lazy. At times, I’m one of those lazy designers. Does that make any of us right or wrong? I don’t think so. Can all of us do better? Most assuredly.

Using design trends helps people. We find out what looks good doesn’t always feel good. We find out that some tools don’t always work for every job. We find out that performance and documentation are more important than your own visual preferences. Design trends, frameworks, and the like are, among other things, learning tools. Let’s keep that in mind.

Sing with me

Everything I do in my work at GitHub and on Bootstrap is dedicated to folks who want to make awesome stuff on the Internet. My dream is to help awesome people do awesome shit. Nothing else gets me more stoked out of my mind than that. I get goosebumps thinking about how much the web has changed and how we can continue to shape it just by writing code and sharing our experiences.

Designers didn’t stop dreaming. We just have millions of people dreaming dreams different than our own. Let’s encourage and foster that by enabling these people, and ourselves, to do more—and to do better. And of course, to dream on.

Including CSS in Jekyll

Jekyll is a blog aware static-site generator. It’s great for building simple pages like Code Guide or blogs like this one. Lately, I’ve been a big fan of Jekyll’s include functionality.

Now, most folks use _includes for standard partials like a site’s header or footer. Lately though, I’ve been using them to easily include CSS dependencies for fewer page requests.

Say you have three CSS files to include in your Jekyll project. Normally you’d do something like this:

<link rel="stylesheet" href="site.css">
<link rel="stylesheet" href="font-awesome.css">
<link rel="stylesheet" href="syntax.css">

With Jekyll, you can turn that into one file during site generation with includes and YAML front-matter. Here’s an example Gist:

The front-matter tells Jekyll to treat this page as a buildable file rather than simply another asset to copy to the generated _site folder. With that in place, add any _includes you like, and run jekyll serve.

Enjoy!

Introducing Poole

Poole is the Jekyll butler. It’s a fully furnished Jekyll setup, designed to be a consistent and flexible starting point for Jekyll projects. It’s available today, and I’m releasing two open source themes with it.

What’s included

Out of the box, Poole is a Jekyll setup that focus on the content first and foremost.

It does so with a limited set of styles and a foundational feature set for any project:

  • Complete Jekyll setup included (layouts, config, 404, RSS feed, posts, and example page)
  • Mobile friendly design and development
  • Easily scalable text and component sizing with rem units in the CSS
  • Support for a wide gamut of HTML elements
  • Related posts (time-based, because Jekyll) below each post
  • Syntax highlighting, courtesy Pygments (the Python-based code snippet highlighter)

Themes

As part of the release of Poole, I’ve created two themes for folks. One, Hyde, has already been available for some time. The second, Lanyon, is brand new today. Each theme includes all the features of Poole, and then some.

Hyde 2

Hyde 1, my first open sourced Jekyll theme, was released in October. Along with the launch of Poole, I’ve redesigned and recoded much of it. Today’s release represents Hyde 2.0. Overall it’s still the same theme, but with many improvements under the hood.

Hyde 2

If you’re wondering, Hyde 1.x is available on my GitHub account. However, it’s no longer being actively maintained.

Lanyon

The second theme, Lanyon, is also being launched today.

Lanyon Lanyon open

Lanyon started off as a variation on Hyde to hide the sidebar. Rather than keep the two together, I figured a brand new theme was in order. Thus, Lanyon was born. The best part is the toggleable sidebar is handled all via CSS—no JavaScript is required.

Theme options

Lanyon in red

Hyde and Lanyon include auto-generated navigation lists with active link highlighting, eight optional color schemes (based on the colors of base16), and reverse layouts (sidebar on the right).

Documentation

Docs for Poole, Hyde, and Lanyon are available in each project’s readme.

The Poole docs include a basic walk-through for setting up Jekyll. Each theme’s readme focuses on only theme-specific features and none of the Jekyll setup process.

Nomenclature

If you’re not familiar with it, The Strange Case of Dr. Jeykll and Mr. Hyde tells the story of a lawyer investigating the connection of two persons, Dr. Henry Jekyll and Mr. Edward Hyde. Chief among the novel’s supporting cast is a man by the name of Mr. Poole, Dr. Jekyll’s loyal butler.

It seemed fitting to name the project after such a man, given all he intends to do—for myself and the rest of you. Plus, it’s a killer naming scheme.

GitHub org

To keep everything neatly organized, everything lives in a new GitHub organization, @poole. It includes Poole, the Poole homepage, and the two themes.

It behooves me to mention that if you have a bug or feature request in any of the Poole-provided files (configs, layouts, poole.css, etc), please open issues in @poole/poole. Anything directly related to the theme (like in hyde.css or lanyon.css), should be addressed in the respective theme’s repository.

Downloads

Snag Poole, Hyde, or Lanyon as you need from each project’s GitHub releases page.

Watch the GitHub repositories to be notified of future downloads, or follow me on Twitter for updates when they become available.

What’s next?

Probably some bug fixes and small feature requests, and soon maybe another simple theme. I plan on looking through my old blog designs for ideas on what to build next. If you have specific ideas, let me know on Twitter.

<3