# 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](#1-tools) 2. [Components](#2-components) - [Modifiers](#modifiers) - [State](#state) - [Media Queries](#media-queries) - [Keeping It Encapsulated](#keeping-it-encapsulated) 3. [JavaScript](#3-javascript) 4. [Mixins](#4-mixins) 5. [Utilities](#5-utilities) 6. [File Structure](#6-file-structure) 7. [Style](#7-style) 8. [Miscellany](#8-miscellany) - [Performance](#performance) ## 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: ``` LESS .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: ``` LESS .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 Sign Up ``` ``` LESS // 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: ``` LESS .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: ``` LESS .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: ``` LESS @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: ``` LESS // 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](http://codepen.io/ericrasch/pen/HzoEx). 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
Gumby
Pat
Add