About Bobby Grace

I'm a designer at Trello Inc. Rounded corners, gradients, that kind of stuff. Follow me on Twitter here.

Refining The Way We Structure Our CSS At Trello

Have you been reading all the blog posts about the CSS architecture at various companies out there? No? Check out the ones for GitHub, CodePen, Lonely Planet, Medium, and Buffer. I can’t get enough of them.

We’ve slowly been refining the way we structure our CSS at Trello. I can firmly say it’s pretty okay now, nearing on good. This is our whole deal with CSS, broken up into the following sections.

  1. The Tools
  2. File Structure
  3. Modules
  4. What about JavaScript?
  5. Styles Style
  6. Numbers
  7. Other Junk
  8. The Future…

Warning: this post assumes you make websites and know some things about CSS. If you don’t, that’s okay. You might still enjoy the peek behind the curtains.

1. The Tools

We use LESS for trello.com. I like it because it’s close to vanilla CSS and intuitive. I’m a simple person with simple needs. I don’t know what I would do with loops and guards and math in CSS but I doubt it would be good. We mostly use imports, data-uri, variables, shallow nesting, and some mixins (primarily for vender-prefixed stuff).

We don’t use Grunt or Gulp. I don’t think anyone is strongly against the idea, but LESS has been good enough.

For a new release, we import all the CSS files into a single core.css file using LESS, then CSSshrink and gzip it, and ship it to our CDN. I wrote a post about our whole design, development, and release process in this blog post, which is only tangentially related but I’m going to plug it anyway.

2. File Structure

How are the files broken down? Here’s a really real look at our core CSS file which has all the imports. It has some organizational warts and inconsistencies, but I hope you’ll appreciate the honest view. I tried to break them into sections a few months ago and they only half make sense. I never looked back.

// bootstrapping
@import (less) "variables_mixins.css";
@import (less) "reset.css";
@import (less) "typography.css";
@import (less) "forms_buttons.css";
@import (less) "icons.css";
@import (less) "content_list.css";
@import (less) "editing.css";

// layout
@import (less) "header.css";
@import (less) "woof.css";
@import (less) "dialog.css";
@import (less) "popover.css";
@import (less) "profiler.css";
@import (less) "boards_sidebar.css";
@import (less) "tabbedpane.css";

// board
@import (less) "board.css";
@import (less) "list.css";
@import (less) "powerup.css";
@import (less) "calendar.css";

// card
@import (less) "card.css";
@import (less) "quickCardEditor.css";
@import (less) "attachments.css";
@import (less) "badges.css";
@import (less) "checklists.css";
@import (less) "labels.css";
@import (less) "datepicker.css";
@import (less) "stickers.css";

// member
@import (less) "member.css";
@import (less) "announcements.css";

// organization
@import (less) "organization.css";

// phenom - acions, notifications, invites
@import (less) "phenom.css";

// pages
@import (less) "manual.css";
@import (less) "account.css";
@import (less) "search.css";
@import (less) "ads.css";

// window sizes
@import (less) "lg.css";
@import (less) "md.css";
@import (less) "sm.css";
@import (less) "touch.css";

// utils
@import (less) "spinner.css";
@import (less) "utils.css";
@import (less) "jquery.crop.css";
@import (less) "tooltip.css";
@import (less) "clipboard.css";

// print
@import (less) "print.css";

We try and keep the files as small and modular as possible so that when there is an occasion to delete a bunch of them, like we did with our meta pages, it’s really easy. There is separate CSS for the meta pages and other packages, but those tend to be small and self-contained.

3. Modules

We try and namespace our CSS. What’s that mean? Instead of using descendant selectors, like .header img { … }, we create a new top level, hypen-separated class for descendant selectors, like .header-image { … }.

I’ll give you a complex example. Descendant selectors would look like this:

.global-header {
  background: hsl(202, 70%, 90%);
  color: hsl(202, 0%, 100%);
  height: 40px;
  padding: 10px;

  .global-header .logo {
    float: left;

    .global-header .logo img {
      height: 40px;
      width: 200px;

  .global-header .nav {
    float: right;

    .global-header .nav .item {
      background: hsl(0, 0%, 90%);
      border-radius: 3px;
      display: block;
      float: left; 
      -webkit-transition: background 100ms;
      transition: background 100ms;

      .global-header .nav .item:hover {
        background: hsl(0, 0%, 80%);

And so forth. With namespacing, it looks like this:

.global-header {
  background: hsl(202, 70%, 90%);
  color: hsl(202, 0%, 100%);
  height: 40px;
  padding: 10px;

  .global-header-logo {
    float: left;

    .global-header-logo-image {
      height: 40px;
      width: 200px;

  .global-header-nav {
    float: right;

    .global-header-nav-item {
      background: hsl(0, 0%, 90%);
      border-radius: 3px;
      display: block;
      float: left; 
      -webkit-transition: background 100ms;
      transition: background 100ms;

      .global-header-nav-item:hover {
        background: hsl(0, 0%, 80%);

This is very efficient because of the low specificity of the selectors. Why? Because browsers read selectors from right to left, meaning the rightmost selector (the key selector) is interpreted first. So you may think everything is all-good-thumbs-up when you write .foo div because there are only a few .foos, but actually you are asking the browser to look up all the tens of millions of divs first, then all the .foos and exponentially more with more descendant selectors. Yipes. For more, check out the CSS Tricks article about efficiently rendering CSS.

I used namespacing in a few places when I spent a week speeding up Trello. It made a noticeable difference, particularly for boards with lots of cards (meaning lots of DOM elements). It might not matter much for smaller sites, but it’s a good practice to get in to.

I also think this makes the CSS more maintainable and readable. It’s obvious that selectors are related. It’s easier to search for selectors and delete them when necessary.

4. What about JavaScript?

I bet your website uses JavaScript. Lots of websites do. We separate our style and behavior concerns by using js- prefixed classes for behavior. Take the following example:

<!-- HTML -->

<div class="content-nav">
  <a href="#" class="content-nav-button js-open-content-menu">

// JavaScript (with jQuery)

$(".js-open-content-menu").on("click", function(e){

How does this work? The .js- class makes it very clear to the next person changing this template that it is being used for some JavaScript event. “Hey, you need me to open the content menu!”, the class says. “Don’t worry, template! I wouldn’t want to mess up the content menu! I’ll make sure you work.” *cheering*

Be sure to use a descriptive class that won’t conflict with other classes. The intent of .js-open-content-menu is more clear than .js-menu which is the wrong kind of lazy. You should never see style classes, like .header-nav-button, in your JavaScript, or behavior classes, like .js-open-header-menu, in your stylesheets. Real talk: we sometimes use style class related to state in our JavaScript, but I’ll get to your proposed fix for that later. But if I see someone put .js- classes in a stylesheet? Noooo. I’ll find you, even if you’re half-way across the world. And when I do, I’ll probably… calmly point to this blog post. I don’t want to start any trouble. Not worth it.

5. Styles Style

There are a hundred million different ways to write CSS and many of them are bad, despite being perfectly valid. Take a little time to format it in a way that makes it easy to work with. Take this example:

.global-header-nav-item {
  background: hsl(0, 0%, 90%);
  border-radius: 3px;
  display: block;
  float: left; 
  -webkit-transition: background 100ms;
  transition: background 100ms;

Here are some key rules we try and stick to…

  • Use a new line for every parameter. Use two new lines between declarations.
  • Add a single space between parameters, for example param: value; and not param:value;.
  • Indent nested rules so it’s obvious they’re related. You’ll be able to identify and skip sections more easily.
  • Alphabetize parameters. I could probably look up how parameters are semantically categorized by position, dimension, typography, color, etc., but I gave up and used the alphabet.
  • Use 2 spaces, not 4 spaces and not tabs.
  • Use hyphens for class names, not underscores or camelCase.
  • Don’t use 0px when 0 will do. Use shorthand when appropriate, like padding: 15px 0; and not padding: 15px 0px 15px 0px;.
  • When using vendor prefixed features, put the standard declaration last. For example… -webkit-transition: all 100ms; transition: all 100ms;. Browsers will optimize the standard declaration, but continue to keep the old one around for compatibility. Putting the standard declaration after the vendor one means it will get used and you get the most optimized version.
  • Use hsl(a) over hex and rgb(a). If I’m twiddling with colors, it’s easier with hsl, especially when making things lighter or darker. If I’m writing hex or rgb, I have to change all the values and it’s harder to guess what the result will look like. I get a better mental picture of a color when I can say “it’s blue, it’s this saturated, and this light” rather than “it’s this much red, this much green, and this much blue.”
  • Comments rarely hurt. If you find an answer online (read: on Stack Overflow) then add the link to a comment so future people know what’s up.

Of course, these are preferences and you don’t need to use any of them. However, your team should stick to your own conventions.

6. Numbers

  • File size: core.css is 43.3kb gzipped and 202kb raw. It still seems like a lot, but maybe not for a single page app? It’s worth noting that we still use an icon font and that it’s embedded in the CSS. It’s about 8kb raw.
  • Lines of LESS: 14,236. That’s across all of our files. I’m using find static/css/ -name '*.css' | xargs wc -l here. We use new lines liberally, though.
  • Number of CSS Files: 44. We import these into a single core.css in the end, but it’s a show of our modularity. Some of those are really big and we should probably break them down. The biggest file is board.css with 1105 lines of LESS. That feels crazy long to me.
  • Number of Selectors: 2,426. How does that stack up? According to Mark Otto, GitHub has around 7,000 selectors, Bootstrap has 1,900, Twitter has 8,900, The New York Times has 2,100, and SoundCloud has 1,100. This is all according to cssstats.com, which you should check out.

7. Other Junk

  • We tend to avoid body classes. We fell prey to this easy hack early on and we’ve been slowly cutting them out. It leads to more descendant selectors, poor browser performance, and it’s a bad replacement for componentizing your stuff.
  • If there’s a small image, say under 10kb, we save a request by embedding it into our CSS using LESS’s data-uri. We replace the urls of images and font assets with our CDN versions before using data-uri.
  • We don’t actively support IE9. That means we can use stuff like flexbox, which we have.
  • Eventually I want to switch from a font over to inline SVGs for icons. We can safely switch because we don’t offer IE9 support. It’s just a big project.
  • We don’t use normalize.css but we probably should. In fact, we don’t use any frameworks or have other external dependencies. Our meta pages use normalize.css and most of HTML5 Boilerplate as a starting point.
  • We don’t lint our CSS, but our LESS compiler fails fast and loud with the slightest deviance. For some reason, I type “displaY” a lot and it blows up on that. We catch a lot of stuff early this way.
  • Autoprefixer sounds good, but we don’t use it. Probably should.

8. The Future…

One downside of our namespacing convention is that we end up with really long selector names that are hard to mentally parse. Modifier and state-related classes, like .global-header-dark, get easily confused for psuedo-descendant classes like .global-header-logo. There’s a more formal technique called BEM, “Block, Element, Modifier”, that addresses this. This CSS Wizardry article does a good job explaining it.

@fat at Medium ran into the same problems. He has a BEM-like technique that’s a little more approachable. Here are the main points…

  • .js- prefixed class names for elements being relied upon for javascript selectors
  • .u- prefixed class name for single purpose utility classes like .u-underline, .u-capitalize, etc.
  • Introduction of meaningful hypens and camelCase — to underscore the separation between component, descendant components, and modifiers
  • .is- prefixed classes for stateful classes (often toggled by js) like .is-disabled
  • New CSS variable semantics: [property]-[value]–[component]
  • Mixins reduced to polyfills only and prefixed with .m-

The whole Medium guide is listed here and it’s full of great tips. We’ve been happy with js-classes and polyfill-only mixins. I plan to use the .is-, .u-, .m-, and –modifier conventions in the future. They seem semantically clearer to me. Thanks, @fat!

I’m also intrigued by the recently proposed Attribute Modules for CSS outlined by Glen Maddern. I wonder what the performance implications for those types of selectors are, though. More food for thought.

That’s our CSS today. If you look at our CSS file (not recommended) you’ll find that we don’t always stick to these conventions. They’ve evolved over time and there’s still some cruft, but we convert things over as we find them. You know how it goes.

Oh, and if you love this kind of stuff as much as I do then you should apply to our Front-End Developer position!

How We Made Our Landing Pages Fast: A Grand Journey

Screen Shot 2014-08-29 at 11.31.04 AM

We recently launched an overhaul to our landing and about pages. These are all the pages that aren’t part of the app, pages like Home, Business Class, Trello Gold, Tour, and Jobs. We call them the “meta” pages because we’re nerds and because it’s not just landing pages, but also things like log-in and sign-up. Take a minute to look around.

At the beginning of our Trelloic journey, we had very few meta pages. Because Trello is a single-page app, we just rolled the meta pages into the client code. That had some obvious problems. It meant you had to download all the JavaScript and CSS and templates for the app just to view something like the Trello Gold page. We made this a little nicer for some pages, but it was still a mess and wasn’t going to scale. Another problem was that these pages didn’t have a consistent look and feel. They were made at different times by different people with different motivations.

We couldn’t let this stand, so we set out to overhaul them. There were two parts of the project: separating the meta page code from the main app, and designing and developing the pages.

Project Metosis

Trello developer and all around good guy Daniel LeCheminant worked on the server component. I called this Project Metosis, a dividing of the “meta” pages, although nobody seemed to care about my clever codename as much as I did. *slumps in chair*

Metosis works like this: when you visit a page like https://trello.com/business-class, there is some server code, some middleware, that looks at the URL and sees if it matches something in the Meta project, which is in a place separate from the client code. If it finds a business-class.html, it short circuits and sends you static files from memory instead of loading the app.

Hold on. Serving static files from a file system? I know. It’s honestly only a little bit faster and fancier than uploading via FTP.

There are over 10,000 benefits to this method, but I’ll highlight the big ones.

  1. We can deploy changes in the Meta project without going through the client release pipeline. Nothing is tied up and we don’t have to release a whole new client just to add a job listing, which we plan to do often apply today for a good job yes.
  2. It’s mega-fast. These are small static files served directly. There’s very little DOM manipulation and the bare minimum CSS, so once it gets to the screen, it renders in a flash.
  3. We only have the HTML, CSS, and JavaScript we need for either the client or meta projects. That means we can cut lotsa code. Our CSS is now 202kb raw and 43.3kb compressed, down from 276kb raw and 58kb compressed. That’s Twenty-Nine Percent (29%) smaller. The reduction in JavaScript was a bit more modest: 443kb down to 410kb, about 8% smaller. Not bad.
  4. You know how nice it is to watch and listen to a quill swiftly write on fresh parchment? You know how it feels to bite into a hot, fresh-out-of-the-oven donut? For developers, safely cutting a bunch of code is better than both of those feelings. This doesn’t really need to be its own item, sorry.
  5. The code is isolated and we don’t have to worry about cascading rules running amok or extraneous scripts bogging down either project. For instance, we noticed that a wild CSS rule on one of our landing pages was causing filtering to be incredibly slow. There should be less of that now.

So Daniel had the “load it fast” part down. Now I needed to make the pages.

Project Marquee

Screen Shot 2014-08-29 at 11.47.54 AM

I called the designing and making of these pages Project Marquee. The name is not as good as Metosis and no one really cared anyway, but whatever. I’m still going to use codenames.

Here’s what I wanted out of the design. I wanted a simple, mobile-friendly layout. I wanted consistent typography. I wanted to use words, but not waste them. I wanted everything up front and visible, nothing buried. Just about the only things you click are links that take you to new pages where you get more information you want. I wanted to show a big screenshot of exactly what Trello looks like so people get it quickly.

Here’s what I didn’t want. I didn’t want any dizzying and confusing scroll effects, images carousels people wouldn’t click through, tabs or hover effects people would miss, or expensive videos people wouldn’t finish. I consider these big, whizz-bang ornaments that obscure the message.

Above all, I wanted to explain what Trello can do for you, convey what people love about it and explain why you might love it, too. I figured I could do that quickly and effectively with some concise words, a few images, and without all the doodads.

The straightforward design made it easy to develop. It wasn’t long before it was ready to ship.


Just before our big announcement, Michael Pryor, CEO of Trello, said if there was anything we wanted to get out we should get it out now. So we wrapped up the last of the project and launched. Then Michael said “No, switch the home page back to the old one” and we were like ”Michael, you just told us to launch” and he said “What if less people sign up with the new page? Better the devil you know…” and we were like “Well. Okay.”

So we converted the old home page to the new meta project, added a bunch of analytics, and A/B tested it. 50% of new people saw the old version, 50% saw the new version, and we recorded which one get more people to sign-up. We found the new page had a sign-up rate of 14% while the old page was around 11%. And it was statistically significant. Great!

It was worth the hassle of setting up the test in the end. We have much better analytics and we are in a better place to test changes in the future. Maybe we’ll test something really out there. Be sure to tell all you friends about trello.com and ask them if they see something cool on the homepage or sign up if you haven’t! (Ed. note: I hope this plan works.)

Why does it get more people to sign up? There’s no way to be 100% sure. It could be because it’s faster. It could be because the pages are better at explaining the product and piquing their interest. Thankfully, we can test more things and get a better sense. Hey, what if we made the pages slower? Think that would get more people to sign up? Who knows…

Really there are two parts of this: getting people to sign up for Trello and then making them happy, active users. The new landing pages get more people into Trello (✓). Now we need to make sure new users have everything they need to be happy and active and get the most out of Trello (without being overly helpful). That’s something we’re working on.

The journey is far from over, but we are in a much better place now. Our landing pages are mega-fast, looking nice and consistent, we can push out updates faster, we can test changes more easily, and the code is cleaner, more maintainable and less error-prone. On to the next adventure!

A Note About Notifications

Screen Shot 2014-08-25 at 10.02.30 AM

We often hear from people that notifications in Trello are noisy and complicated, which ultimately lessens their value. We want notifications to really matter, so we trimmed down the quantity and centered the system around the notification that everyone agreed mattered the most: being mentioned on a comment.

You won’t get some of the old notifications, like checklist item completes or description changes. So if you want to let someone know that something is important, mention them in a comment and they will be sure to be notified. We’ve been making notifying members easier with a more visible “mention members” button when commenting, a new reply button on comments in the activity feed, and @-style autocomplete.

So what triggers notifications now?

When subscribed to a card, you’ll get notifications for…

  • All comments
  • Adding, changing, and upcoming due dates
  • Card moves and archives

When subscribed to a list or board

  • You’ll get the same notifications listed above, but for all cards in a list or board.
  • You’ll also get notifications for all newly created cards

And at any time, you’ll get notifications when…

  • You are mentioned in a comment.
  • You are added to a card, board, or organization.
  • You are mentioned in a checklist item.
  • One of your boards is closed.
  • When someone makes you an admin of a board or organization.

Update: when we originally launched with this change, board and list subscribe worked differently and didn’t give you notifications about card details like comments and due dates. We’ve made a change based on your feedback. Now subscribing to a board or list is exactly like subscribing to all the cards in that board or list.

There’s another slight change regarding email notifications. Previously, the default was to batch notifications together and send a summary email about every hour. We did it this way because the notifications were so abundant and overwhelming. Now that there are fewer, the new default is set to instant. Combined with the ability to reply via email, we think it will allow for quicker and more seamless discussions. If you want to change your notifications setting to instant, you can do that on your settings page under “Notifications” > “Change Notification Email Frequency”.

We have a lot of respect for your time and attention. When we buzz your phone, desktop, email app, or watch, we hope it’s for something that really matters.

In a rare, behind-the-scenes moment, there’s another piece of the notifications puzzle that we’re working on: reminders. Currently, due date notifications arrive 24 hours before the due date. It’s not a time that works for everyone and there is no way to change it. We want to split up due date notifications into a feature called reminders. You can set a reminder independent of a due date, so that you can simply say, “give me a notification about this card at such and such time”. When you set a due date, it would automatically set a reminder, but it will be editable. Reminders will be per-member. Setting a due date will add a reminder for everyone on the card, but they will be able to change or remove their own reminders. Warning, though: the details of this may change and we don’t have a timeline for the release. Let us know what you think on the Trello Development board!

Hello, Saved Searches

Saved Searches

When we introduced the new search back in May, people from around the world were astonished by the speed and flexibility of Trello search. By that I mean it was no longer slow and broken. Some time passed. The cheering we heard in our heads calmed to an excited murmur. We stepped back and thought, “What else can we do with search?”

The new search operators with autocomplete are one of the nicest things in new search. With operators like “@me”, “-list:done”, “due:week”, and “-has:members”, you can create highly tailored lists for all kinds of purposes. You can answer, “What’s due this week which has no one assigned to it?” and “What do I need to do today?” and “What cards have stickers, covers, have ‘Garfield’ in the title and were edited in the last 24 hours?”.

Wouldn’t it be awesome if you could easily get back to searches you use frequently?

I think you know what I’m getting at.

You can now save searches. It’s easy: just start searching, maybe add an operator or two, then click the “Save This Search” link in the top right and give it a name. Now whenever you click the search box, your saved searches will be at the top. You can rearrange them by dragging and dropping and you can delete them at will.

This is a feature for people with Trello Gold and Business Class. We think it’s something people managing lots of cards and boards will find useful. Remember, you can get Trello Gold for free just by recommending Trello to people!

Now go find those cards.

Hello, Quick Card Editor


Have you ever noticed a typo in your card name and wanted to change it without opening the card, clicking the card title, editing, etc. and when you realized you couldn’t you threw your hands up in the air in frustration, walked out of the room and onto your 300ft luxury yacht and sailed to Bermuda?

Well come back home! Now you can edit card names without opening the card.

It works like this: when you hover over a card, you’ll see an edit icon in the top right corner. Click it and you’ll start editing the card in place. You’ll also be able to edit labels, members, stickers, and the like.

“Excuse me. I don’t use mice. Is there a keyboard shortcut? If there isn’t, I swear I’ll climb into SS Magnificence and you won’t hear from me for three weeks AT LEAST.” *does a 180 and heads for the door*

Whoa whoa whoa, come back! There is a shortcut. It’s the “e” button.

The quick card editor is available today on trello.com. Go try it out.

Hey, does your boat have wifi?