Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Trello CSS Guide

Hello, visitors! If you want an updated version of this styleguide in repo form with tons of real-life examples… check out Trellisheets! https://github.com/trello/trellisheets


Trello CSS Guide

“I perfectly understand our CSS. I never have any issues with cascading rules. I never have to use !important or inline styles. Even though somebody else wrote this bit of CSS, I know exactly how it works and how to extend it. Fixes are easy! I have a hard time breaking our CSS. I know exactly where to put new CSS. We use all of our CSS and it’s pretty small overall. When I delete a template, I know the exact corresponding CSS file and I can delete it all at once. Nothing gets left behind.”

You often hear updog saying stuff like this. Who’s updog? Not much, who is up with you?

This is where any fun you might have been having ends. Now it’s time to get serious and talk about rules.

Writing CSS is hard. Even if you know all the intricacies of position and float and overflow and z-index, it’s easy to end up with spaghetti code where you need inline styles, !important rules, unused cruft, and general confusion. This guide provides some architecture for writing CSS so it stays clean and maintainable for generations to come.

There are eight fascinating parts.

  1. Tools
  2. Components
  3. JavaScript
  4. Mixins
  5. Utilities
  6. File Structure
  7. Style
  8. Miscellany

1. Tools

Use only imports, variables, and mixins (and only for vender-prefixed features) from CSS preprocessors.

To keep our CSS readable, we try and keep our CSS very vanilla. We use LESS, but only use imports, data-uri, variables, and some mixins (only for vender-prefixed stuff). We use imports so that variables and mixins are available everywhere and it all outputs to a single file. We occasionally use nesting, but only for very shallow things like &:hover. We don’t use more complex functions like guards and loops.

If you follow the rest of the guide, you shouldn’t need the complex functions in preprocessors. Have I piqued your interest? Read on…

2. Components

Use the .component-descendant-descendant pattern for components.

Components help encapsulate your CSS and prevent run-away cascading styles and keep things readable and maintainable. Central to componentizing CSS is namespacing. Instead of using descendant selectors, like .header img { … }, you’ll create a new hyphen-separated class for the descendant element, like .header-image { … }.

Here’s an example with descendant selectors:

.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 here’s the same example with namespacing:

.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 {
      background: url("logo.png");
      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%);
    }

Namespacing keeps specificity low, which leads to fewer inline styles, !important declarations, and makes things more maintainable over time.

Make sure every selector is a class. There should be no reason to use id or element selectors. No underscores or camelCase. Everything should be lowercase.

Components make it easy to see relationships between classes. You just need to look at the name. You should still indent descendant classes so their relationship is even more obvious and it’s easier to scan the file. Stateful things like :hover should be on the same level.

Modifiers

Use the .component-descendant.mod-modifier pattern for modifier classes.

Let’s say you want to use a component, but style it in a special way. We run into a problem with namespacing because the class needs to be a sibling, not a child. Naming the selector .component-descendant-modifier means the modifier could be easily confused for a descendant. To denote that a class is a modifier, use a .mod-modifier class.

For example, we want to specially style our sign up button among the header buttons. We’ll add .global-header-nav-item.mod-sign-up, which looks like this:

<!-- HTML -->

<a class="global-header-nav-item mod-sign-up">
  Sign Up
</a>
// global-header.less

.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.mod-sign-up {
  background: hsl(120, 70%, 40%);
  color: #fff;
}

We inherit all the global-header-nav-item styles and modify it with .mod-sign-up. This breaks our namespace convention and increases the specificity, but that’s exactly what we want. This means we don’t have to worry about the order in the file. For the sake of clarity, put it after the part of the component it modifies. Put modifiers on the same indention level as the selector it’s modifying.

You should never write a bare .mod- class. It should always be tied to a part of a component. .header-button.mod-sign-up { background: green; } is good, but .mod-sign-up { background: green; } is bad. We could be using .mod-sign-up in another component and we wouldn’t want to override it.

You’ll often want to overwrite a descendant of the modified selector. Do that like so:

.global-header-nav-item.mod-sign-up {
  background: hsl(120, 70%, 40%);
  color: #fff;

  .global-header-nav-item-text {
    font-weight: bold;
  }

}

Generally, we try and avoid nesting because it results in runaway rules that are impossible to read. This is an exception.

Put modifiers at the bottom of the component file, after the original components.

State

Use the .component-descendant.is-state pattern for state. Manipulate .is- classes in JavaScript (but not presentation classes).

State classes show that something is enabled, expanded, hidden, or what have you. For these classes, we’ll use a new .component-descendant.is-state pattern.

Example: Let’s say that when you click the logo, it goes back to your home page. But because it’s a single page app, it needs to load things. You want your logo to do a loading animation. This should sound familiar to Trello users.

You’ll use a .global-header-logo-image.is-loading rule. That looks like this:

.global-header-logo-image {
  background: url("logo.png");
  height: 40px;
  width: 200px;
}

.global-header-logo-image.is-loading {
  background: url("logo-loading.gif");
}

JavaScript defines the state of the application, so we’ll use JavaScript to toggle the state classes. The .component.is-state pattern decouples state and presentation concerns so we can add state classes without needing to know about the presentation class. A developer can just say to the designer, “This element has an .is-loading class. You can style it however you want.”. If the state class were something like global-header-logo-image--is-loading, the developer would have to know a lot about the presentation and it would be harder to update in the future.

Like modifiers, it’s possible that the same state class will be used on different components. You don’t want to override or inherit styles, so it’s important that every component define its own styles for the state. They should never be defined on their own. Meaning you should see .global-header.is-hidden { display: none; }, but never .is-hidden { display: none; } (as tempting as that may be). .is-hidden could conceivably mean different things in different components.

We also don’t indent state classes. Again, that’s only for descendants. State classes should appear at the bottom of the file, after the original components and modifiers.

Media Queries

Use media query variables in your component.

It might be tempting to add something like a mobile.less file that contains all your mobile-specific rules. We want to avoid global media queries and instead include them inside our components. This way when we update or delete a component, we’ll be less likely to forget about the media rules.

Rather than writing out the media queries every time, we’ll use a media-queries.less file with media query variables. It should look something like this:

@highdensity:  ~"only screen and (-webkit-min-device-pixel-ratio: 1.5)",
               ~"only screen and (min--moz-device-pixel-ratio: 1.5)",
               ~"only screen and (-o-min-device-pixel-ratio: 3/2)",
               ~"only screen and (min-device-pixel-ratio: 1.5)";

@small:        ~"only screen and (max-width: 750px)";
@medium:       ~"only screen and (min-width: 751px) and (max-width: 900px)";
@large:        ~"only screen and (min-width: 901px) and (max-width: 1280px)";
@extra-large:  ~"only screen and (min-width: 1281px)";

@print:        ~"print";

To use a media query:

// Input
@media @large { 
  .component-nav { … }
}

/* Output */
@media only screen and (min-width: 901px) and (max-width: 1280px) {
  .component-nav { … }
}

You can use commas to include multiple variables, like @media @small, @medium { … }.

This means we’re using the same breakpoints throughout and you don’t have to write the same media query over and over. Repeated phrases like media queries are easily compressed so you don’t need to worry about CSS size getting too big. This practice was taken from this CodePen from Eric Rasch.

Note that print is a media attribute, too. Keep your print rules inside components. We don’t want to forget about them either.

Put media rules at the bottom of the component file.

Keeping It Encapsulated

Components can control a large part of the layout or just a button. In your templates, you’ll likely end up with parts of one component inside another component, like a .button inside a .member-list. We need to change the button’s size and positioning to fit the list.

This is tricky. Components shouldn’t know anything about each other. If the smaller button can be reused in multiple places, add a modifier in the button component (like, .button.mod-small) and use it in member-list. Do the positioning with a member list component with a descendant, since that’s specific to the member list and not the button.

Here’s an example:

<!-- HTML --> 

<div class="member-list">
  <div class="member-list-item">
    <p class="member-list-item-name">Gumby</p>
    <div class="member-list-item-action">
      <a href="#" class="button mod-small">Add</a>
    </div>
  </div>
</div>
// button.less

.button {
  background: #fff;
  border: 1ps solid #999;
  padding: 8px 12px;
}

.button.mod-small {
  padding: 6px 10px;
}


// member-list.less

.member-list {
  padding: 20px;
}

  .member-list-item {
    margin: 10px 0;
  }

    .member-list-item-name {
      font-weight: bold;
      margin: 0;
    }

    .member-list-item-action {
      float: right;
    }

A bad thing to do would be this:

<!-- HTML -->

<div class="member-list">
  <div class="member-list-item">
    <p class="member-list-item-name">Pat</p>
    <a href="#" class="member-list-item-button button">Add</a>
  </div>
</div>
// member-list.less

.member-list-item-button {
  float: right;
  padding: 6px 10px;
}

In the bad example, .member-list-item-button overrides styles specific to the button component. It assumes things about button that it shouldn’t have to know anything about. It also prevents us from reusing the small button style and makes it hard to clean or change up later if needed.

You should end up with a lot of components. Always be asking yourself if everything inside a component is absolutely related and can’t be broken down into more components. If you start to have a lot of modifiers and descendants, it might be time to break it up.

3. JavaScript

Separate style and behavior concerns by using .js- prefixed classes for behavior.

For example:

<!-- HTML -->

<div class="content-nav">
  <a href="#" class="content-nav-button js-open-content-menu">
    Menu
  </a>
</div>
// JavaScript (with jQuery)

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

Why do we want to do this? The .js- class makes it clear to the next person changing this template that it is being used for some JavaScript event and should be approached with caution.

Be sure to use a descriptive class name. The intent of .js-open-content-menu is more clear than .js-menu. A more descriptive class is less likely to conflict with other classes and it’s lots easier to search for. The class should almost always include a verb since it’s tied to an action.

.js- classes should never appear in your stylesheets. They are for JavaScript only. Inversely, there is never a reason to see presentation classes like .header-nav-button in JavaScript. You will see state classes like .is-state in your JavaScript and your stylesheets as .component.is-state.

4. Mixins

Prefix mixins with .m- and only use them sparingly for shared styles.

Mixins are shared styles that are used in more than one component. Mixins should not be standalone classes or used in markup. They should be single level and contain no nesting. Mixins make things complicated fast, so use sparingly.

Previously, we used mixins for browser prefixed features, but we use autoprefixer for that now.

When using a mixin, it should include the parenthesis to make it more obvious that it’s a mixin. Example usage:

// mixins.less
.m-list-divider () {
  border-bottom: 1px solid @light-gray-300;
}

// component.less
.component-descendent {
  .m-list-divider();
}

5. Utilities

Prefix utility classes with .u-.

Sometimes we need a universal class that can be used in any component. Things like clear fixes, vertical alignment, and text truncation. Denote these classes by prefixing them with .u-. For example:

.u-truncate-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

All the utils should be in a single file. There shouldn’t be any need to overwrite them in components or mixins.

You should really only need a few utilities. We don’t need something like .u-float-left { float: left; } where including float: left; in the component is just as easy and more visible.

6. File Structure

The file will look something like this:

@charset "UTF-8"

@import "normalize.css"

// Variables
@import "media-queries.less"
@import "colors.less"
@import "other-variables-like-fonts.less"

// Mixins
@import "mixins.less"

// Utils
@import "utils.less"

// Components
@import "component-1.less"
@import "component-2.less"
@import "component-3.less"
@import "component-4.less" // and so forth

Include normalize.css at the top of the file. It standardizes CSS defaults across browsers. You should use it in all projects. Then include variables, mixins, and utils (respectively).

Then include the components. Each component should have its own file and include all the necessary modifiers, states, and media queries. If we write components correctly, the order should not matter.

This should output a single app.css file (or something similarly named).

7. Style

Even following the above guidelines, it’s still possible to write CSS in a ton of different ways. Writing our CSS in a consistent way makes it more readable for everyone. Take this bit of CSS:

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

It sticks to these style rules:

  • Use a new line for every selector and every declaration.
  • Use two new lines between rules.
  • Add a single space between the property and value, for example prop: value; and not prop:value;.
  • Alphabetize declarations.
  • Use 2 spaces to indent, not 4 spaces and not tabs.
  • No underscores or camelCase for selectors.
  • 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;. (Note: 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.)
  • Prefer hsl(a) over hex and rgb(a). Working with colors in code is easier with hsl, especially when making things lighter or darker, since you only have one variable to adjust.

8. Miscellany

You might get the impression from this guide that our CSS is in great shape. That is not the case. While we’ve always stuck to .js classes and often use namespaced-component-looking classes, there is a mishmash of styles and patterns throughout. That’s okay. Going forward, you should rewrite sections according to these rules. Leave the place nicer than you found it.

Some additional things to keep in mind:

  • Comments rarely hurt. If you find an answer on Stack Overflow or in a blog post, add the link to a comment so future people know what’s up. It’s good to explain the purpose of the file in a comment at the top.
  • In your markup, order classes like so <div class="component mod util state js"></div>.
  • You can embed common images and files under 10kb using datauris. In the Trello web client, you can use embed(/path/to/file) to do this. This saves a request, but adds to the CSS size, so only use it on extremely common things like the logo.
  • Avoid body classes. There is rarely a need for them. Stick to modifiers within your component.
  • Explicitly write out class names in selectors. Don’t concatenate strings or use preprocessor trickery to build a class name. We want to be able to search for class names and that makes it impossible. This goes for .js- classes in JavaScript, too.
  • If you are worried about long selector names making our CSS huge, don’t be. Compression makes this a moot point.

Some additional reading on CSS architecture around the web:

Performance

Performance probably deserves it’s own guide, but I’ll talk about two big concepts: selector performance and layouts/paints.

Selector performance seems to matters less and less these days, but can be a problem in a complex, single-page app with thousands of DOM elements (like Trello). The CSS Tricks article about selector performance should help explain the important concept of the key selector. Seemingly specific rules like .component-descendant-descendant div are actual quite expensive in complex apps because rules are read from right to left. It needs to look up all the divs first (which could be thousands) then go up the DOM from there.

Juriy Zaytsev’s post on CSS profiling profiles browsers on selector matching, layouts, paints, and parsing times of a complex app. It confirms the theory that highly specific selectors are bad for big apps. Harry Roberts of CSS Wizardry also wrote about CSS selector performance.

You shouldn’t have to worry about selector performance if you use components correctly. It will keep specificity about as low as it gets.

Layouts and paints can cause lots of performance damage. Be cautious with CSS3 features like text-shadow, box-shadow, border-radius, and animations, especially when used together. We wrote a big blog post about performance back in January 2014. Much of this was due to layout thrashing caused by JavaScript, but we cut out some heavy styles like borders, gradients, and shadows, which helped a lot.

Would be helpful for readability to specify the code block languages here — e.g. :

``` CSS
.style-guide-selector {
rjanot commented Feb 11, 2015

Impressed by this guide, i will use it in my company !

+1 Will use it for sure!!

Owner

@ehmorris TIL… :) Updated.

(Y)

jlukic commented Feb 12, 2015

Agree with nearly everything. So many things similar to Semantic UI, just with very different ideas about naming conventions.

To keep our CSS readable, we try and keep our CSS very vanilla. We use LESS, but only use imports, data-uri, variables, and some mixins (only for vender-prefixed stuff).

Same, exactly. Less only with variables and imports. We use autoprefixer though instead of mix-ins. Handles just about everything you could need.

You should never write a bare .mod- class. It should always be tied to a part of a component. .header-button.mod-sign-up { background: green; } is good, but .mod-sign-up { background: green; } is bad.

Same, except modifiers are called 'variations' and 'types' depending on whether they are mutually exclusive (can be stacked together) or inclusive. All modifiers only have meaning in the context of components. This is similar to grammatical modifiers, aka "a big planet" versus "a big ant".

Rather than writing out the media queries every time, we’ll use a media-queries.less file with media query variables.

Same except we use names like @smallestTablet and @largestMobileScreen to help clarify.

Like modifiers, it’s possible that the same state class will be used on different components. You don’t want to override or inherit styles, so it’s important that every component define its own styles for the state.

Same, state is a part of every component definition and strictly tied to behavioral definitions (aka the js stuff)

Make sure every selector is a class. There should be no reason to use id or element selectors. No underscores or camelCase. Everything should be lowercase.

Same, except in addition, we make sure every class is a real english word with hyphens only if the english word is also hyphenated.


All in all thanks for sharing. Love trello. You're killing it with your product design. Best of luck!

Very good style guide!

Just one question, about Keeping It Encapsulated, say I want only the button inside member list to have some margin or padding (which is specific to the member list and very unlikely used in other parts of the site). Personally, I'll add . member-list-item-action .button, but what's your take on that?

"Keeping It Encapsulated"..... thank you for this, will help a lot

Owner

@benhanks040888 The interdependency of components is the trickiest part of all this. Components shouldn't know anything about each other, but some times they have to interact. When they do, very little information should be exchanged. Writing .member-list-item-action .button means they know a lot about each other. Writing .button.mod-small means they know pretty much nothing. It's also easy to search for and delete when the time comes, and as a bonus it's something you can reuse later, too.

pketh commented Feb 12, 2015

This looks super great!
The one thing that sticks out to me is that you guys aren't using autoprefixer? It sounds like you might be able to do away with a whole bunch of mixins and noise on your LESS by adding it to the build process.

Speaking of the build process, how are LESS files compiled up at Trello? I remember you mentioning in an earlier blog post that you weren't using a centralized build tool (Grunt, etc.) at the time. Is this still the case?

Great rundown!

Owner

@pketh we have a custom script that handles all the compiling, gzipping, and sending to s3 and watches for changes while developing and all that. I would like to use autoprefixer on the web client. Mixins have mostly worked for us, but now that we’re using flexbox more and more, it’s beginning to be a pain. I’m using autoprefixer and gulp on new projects and it’s been great.

leogono commented Feb 18, 2015

In this example

.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 {
      background: url("logo.png");
      height: 40px;
      width: 200px;
    }

  .global-header-nav {
    float: right;
  }

I'm thinking of using the following instead:

.global-header {
  background: hsl(202, 70%, 90%);
  color: hsl(202, 0%, 100%);
  height: 40px;
  padding: 10px;
  &-logo {
    float: left;
    &-image {
      background: url("logo.png");
      height: 40px;
      width: 200px;
    }
  }
  &-nav {
    float: right;
  }
}

it will work the same as above but you'll save some keys in typing. :) I think it's also as readable as above.

leogono commented Feb 18, 2015

Great guide, by the way. :)

Owner

@leogono Thanks!

Writing it that way makes the classes hard to search for later. I can do ack global-header-logo-image and go straight to the line, whereas if I did ack image, dozens of results could show up.

@bobbygrace, nice work!

@leogono, another issue with nesting with ampersands is that you can't reference any of those nested classes from within other classes. For example you can't do this:

.something {
  &-child { ... }
}
// Later on...
.something-else {
  .something-child(); // This doesn't work...
                      // technically there isn't anything named ".something-child".
}

why do you use gist ? what about repository ? :)

This makes a lot of good points but I'm curious why you don't favour BEM for naming components? It seems to address the same concerns you have with your naming convention but has the benefit of being more explicit in its conventions.

What about grid classes?
<div class="column? component mod util state js"></div>

<div class="row member-list">
  <div class="col-6 member-list-item">
    <p class="member-list-item-name">Gumby</p>
  </div>

  <div class="col-6 member-list-item">
    <p class="member-list-item-name">Ybmug</p>
  </div>
</div>

or adding extra markup for grid and nest components inside?

<div class="member-list">
  <div class="row">
    <div class="col-6">
      <div class="member-list-item">
        <p class="member-list-item-name">Gumby</p>
      </div>
    </div>

    <div class="col-6">
      <div class="member-list-item">
        <p class="member-list-item-name">Gumby</p>
      </div>
    </div>
  </div>
</div>

Did you try, or think about, defining an order for properties ? If Yes, did it work ?
I find this easier when reading CSS, espcially when it has been coded by other 😄

Example :

.component {
    content: '';            //when needed
    position: absolute;     //positioning
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    display: block;         //display and gaps
    margin: 0px;
    padding: 0px;
    border: 1px solid $red;
    background: $blue;      //Coloring
    color: $green;          //Yes this component will be bright
    text-align: center;     //Last styles
    text-shadow: ...;
}

by the way, great job !

Owner

@Alexgalinier There didn't seem to be a purely semantic way to order them, so I just went alphabetical. It's much easier to find stuff.

Owner

@ivnmaksimovic If we’re following the encapsulated components ideas here, then the column would its own component and member-list would be a modifier. Grids aren’t special; there’re just a component, ya know?

Owner

@davidrapson But BEM doesn’t address the naming issues I have with BEM! The conventions are more explicit, but the output is less human readable. You can get an idea of what mod-, is-, u-, and js- prefixed classes mean without having to read a tutorial.

We've been using this style guide for a couple of weeks now. So far, it's been great. It gives us a good methodology for writing css and the confidence to make changes later. Thanks for sharing.

@Alexgalinier We're using CSSComb (https://github.com/csscomb/csscomb.js) to enforce property sorting and formatting/conventions. It can sometimes lead to funky diffs when we forget to use it, but it's incredibly useful for our CSS readability.

kaelig commented Jul 30, 2015

Thanks for sharing. I am under the impression that there is no linting tooling that enforces these rules, is that right?

hkongm commented Jul 31, 2015

media query part is awesome

jusfeel commented Aug 4, 2015

I just love everything about Trello.

mkiramu commented Aug 6, 2015

Excellent read.

I wholeheartedly disagree with using spaces over tabs, but I digress that coding standards are a team decision. I think this guide shows some real research effort, and I will likely keep this as a reference for any of my future style guides.

Good work.

eru-rg commented Aug 14, 2015

I'm too luv hsl() colors!

Owner

@kaelig We don't have linting rules at the moment, though we've talked about it. There’s enough legacy CSS on our codebase that it would be too much noise unless we spent a lot of time doing a major refactor.

Great read so far.. just want to comment on something that seems conflicting, when you say "We occasionally use nesting, but only for very shallow things like &:hover", and then at the end of the next section it says "Stateful things like :hover should be on the same level.", and that immediately threw me off. I might change the first sentence to read "but only with caution in specific instances" (or something like that) to keep it more general.

Beautiful!

I'd really like to hear from the author why he'd use spaces rather than tabs.

Great stuff :-)

lewisle commented Nov 15, 2015

Brilliant! 👍

👍 Nice!

selbekk commented Nov 18, 2015

What are your views on using next sibling selectors etc? We're following these guidelines on a project I'm working on atm, and I found myself having to deviate partially from these guidelines in order to create checkboxes etc. Here's a codepen.

Owner

@igorcadelima purely a preference that the whole team agrees on. It's also a lot easier to read with deep nesting.

Owner

@selbekk I think that works okay; its still encapsulated. It was pretty hard for me to read. Here's an edit with same proper namespacing: https://codepen.io/anon/pen/ZbNjBr

luck2011 commented Jan 7, 2016

Can't agree more! Except hyphen-separated class deeper than 4th depth like .global-header-nav-item-text

Maybe we should find another proper name making sense like .global-header-blog, .global-header-forum. 💯

tjwudi commented Jan 7, 2016

A clear, easy guide. Thanks for this! I am concerned about overlapping between utilities and mixins though, since both of them help to share common styles. Let's still use clearfix as example, you could use it either as a mixin or utility.

I prefer use mixins always, since it is

  1. Way more flexible. You could use variables.
  2. 99% of utility classes are really small that you don't actually need to save bytes of repeating it in the compiled code

Utility classes seems to be more convenient when there is no CSS pre-processor.

This is awesome! Especially in the prefix of js-, m-.
I will recommend it to my team.
👏

Owner

@tjwudi there's a lot of overlap between mixins and utilities, yes. A lot of time you should just use a mixin or component, but in practice there are times where you want to use something like .u-text-align-center or .u-quiet-text and don't need a full component. We only have about 15 utility classes in the Trello web client, for what it's worth. I don't think we'll add any more. We're working to reduce that number.

This guide has helped me tremendously for over a year now.

I do feel the need, though, to come back and say something against the use of js-* classes. Why not use data-* attributes instead? In my opinion, they offer more of a 'true' separation between presentational and behavioral concerns. For example, I enjoy using a couple of generic event handler functions that trap click for a[data-ajax] and form[data-ajax] and pass the events on to jquery-pjax or a custom handler, depending on flags in the attribute that can be checked like so: $this.is('[data-ajax~=push]'). Instead of .is-loading or .is-expanded for 'state-based styling', they simply become [data-loading] or [data-expanded].

The one problem I have with this, is the inevitable situation of multi-word components. Trello, while feature rich, has very few actual components. In contrast, other applications might have many, many more components (talking 30+). In these situations, oftentimes the best name for a component is comprised of multiple words.

How would the .component-descendant-descendant naming convention work, for situations where .component is a .schedule-editor and the schedule editor has a .date-picker as a descendant?

Owner

@bryanerayner Trello has 113 components! Components can be big or small. They can describe whole layouts or something small like the badges on the front of the card. You'll have different components nested inside each other. In your case, breaking out .date-picker into its own component would be totally fine.

Owner

@tuespetre I suppose you could do that. We style everything with classes, though. Using classes for state-based styling makes dealing with specificity easier.

Using .js-* classes makes it more clear that it's being used for JavaScript. A data attribute signals that you’re storing data. We also use .js-* classes for more than data/ajax-y things; it could just open a menu, for instance. ITOH classes are supposed to be used for styling, so it's a hack any way you slice it.

thierryk commented Mar 8, 2016

@bobbygrace

Please do not show example like this:

<a href="#" class="button mod-small">Add</a> 

either use <button> or include role="button"

Thanks!

Owner

@thierryk You’re totally right. We should switch to using button for those types of things.

XOP commented Mar 23, 2016

Hey @bobbygrace, well written!

It's been a pleasure to read and realize we are on the same page about so many aspects of CSS styleguide.
In my version there are some differences, but not critical, I understand many historical and traditional reasons backing these or that decisions.
For instance, we use rules grouping pattern instead of alphabetical order, we use component--mod and component--is-state patterns for classnames, etc, but generally this does not matter much.

I would like to borrow some lines from your guide about utilities rules, I think that's awesome notes!

And one more proposition about structure. Since it's not described here, I will move it to the issues, hope you will find it useful.

Thanks!

Owner

@XOP thanks! I totally stole the utilities idea from the Medium CSS guide. The whole thing is worth a read: https://medium.com/@fat/mediums-css-is-actually-pretty-fucking-good-b8e2a6c78b06#.c0jyh047z You'll probably find more ideas in there.

nwshane commented Apr 4, 2016

I have a question about component modifiers. Is it okay to use multiple modifiers on one component? As in the following example:

.link { ... }

.link.mod-big {
  font-size: 2rem;
}

.link.mod-underlined {
  text-decoration: underline;
}

<a href='#' class='link mod-big mod-underlined'>
  Click me because I'm big and underlined!
</a>

I started using this style guide about six months ago and it's made an enormous difference in the quality of my CSS. Thanks Trello!

Owner

@nwshane Yup, we do that. Probably don't want one for every property, though.

I'm glad it's made a difference!

@bobbygrace - Hello, I'm impressed with this guideline. So, I translated it to Japanese. Is it no problem with you?

https://gist.github.com/naru0504/733f516e31a4b2571c3ace3bd47eda51

Owner

@naru0504 that's cool!

Converting

.global-header {
}
.global-header .logo {
}

to

.global-header {
}
   .global-header-logo {
   }

These two CSS blocks don't mean the same thing.

The former case actually represents a parent - descendant relation while the latter does not represent any relation although you intend to use them as parent-descendant. The former case goes in synch with your HTML as per CSS standards but latter does not.

tompazourek commented May 24, 2016 edited

@bobbygrace What are you thoughts on nesting two components inside each other? What if I need some special CSS rules that are specific to the parent-child relationship of those two nested components? Do you still avoid rules like .component1 .component2 in those cases?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment