Every developer can write CSS, the hard part is keeping things manageable as your app starts to grow.
Over the past two years I've been writing CSS for several large web applications. I've been fortunate enough to be the primary maintainer of these code bases, which has given me free reign to try out various approaches in an attempt to keep things sensible. In this post I will will share some of my techniques which I feel really made a difference.
If there's one single thing that really made my life easier it's preprocessors. We chose to use SASS on the projects I worked on. I had used LESS in the past, but chose to start using SASS mainly because it's all I ever heard other CSS devs talk about, and also due to this post by Chris Coyier.
Aside from which preprocessor you choose to use, I will become your closest ally in helping you to fight the battle for sensible CSS. The tips that follow will assume that you are using a preprocessor. Examples will be in SASS, but most should be easily transferable to other languages.
2. Standard padding
Throughout our app we need to use padding and margins to space things out. In apps I've worked on in the past these values have all to often just been random pixel values that looked close to the mockup at the time.
Keep all spacing in your app looking consistent by introducing padding variables. I like to keep these 'app wide' variables in a single file called
Throughout your app make it a rule that every padding or margin definition must use the standard variables.
If you're targeting relatively modern browsers, or aren't using a pre-processor, you could also look at using
remunits to do something similar.
3. Standard colours
Every professional application has a colour pallet. Don't bother typing those horrible hex codes all over your app. Instead define them in once inside your
vars.scss file and use the variables throughout your app code.
It's now a breeze if your designer wants to alter the color pallet slightly.
With CSS preprocessors comes the ability to nest selectors. With all new things, it's easy to get carried away. I like to keep it to a minimum, attempting not to nest deeper than three levels. I name my classes in a way that means that I can write really short, specific selectors.
This is good for selector matching performance (negligible) and means elements remain portable. By 'portable' I mean that if markup changes slightly, you don't have to go through altering selectors that were previously very specific, so that they match the new document structure.
The first step to sensible CSS is keeping files as short as possible. I want each file to cover a very specific part of my application, there should be no rules inside that file that do not concern that part.
Let's have a look at a fictitious
header.scss file as an example.
I know what you're thinking: 'this is ludicrous, an entire file for one CSS rule!'. Relax...this this is fine! We're using a preprocessor that will combine all these files into one file, so there really is no overhead to creating more files. I've worked on projects with 100 - 150
.scss files, and it was one of the most effective ways of keeping things manageable.
What we're getting here is 'separation of concern', and this is super valuable! Although right now our
header.scss file is really short, who knows how it will evolve over the course of the applications life. Whenever I start styling a new part of an application, the first thing I do is create a new file for it. The more files you have, the better!
Not all the files in you application are going to be as short as
header.scss above. When files do begin to grow, it is important to structure them consistently so devs know where to find rules. To help with this, I stick by the simple convention of ordering my selectors in the same order that they appear in the markup.
If the markup looks like this,
... the corresponding stylesheet should look like this,
At some point you're going to have to override the default styling of an element when your app is in a specific state. 'State' could be anything, but I'll use the simple example of a disabled button. Our default styling for the button is active, then we have a selector that overrides the default appearance to give it a 'disabled' appearance. One way of structuring this that I'm sure you've all come across is this:
This is a nightmare. If I've be given the task to work on
.my-button I want to go the the initial selector and see all the possible states that element can be in, in one place. I am then able to make informed judgements when taking care of my task.
I also included a handy divider that gives pre-warning to other developers that I'm defining a stateful override based on the
I wrote a more detailed post on 'Styling state in CSS'
JS only classes
In CSS we use the un-prefixed class. Styling using the
js-* classes is strictly forbidden.
By writing the above CSS the developer has made the assumption that every
<button> element in the application must have the following styles. Now every-time I want to use a button element I find myself reseting these styles.
If we have third-party widgets in our app, they too will be impacted by these global styles.
I usually try to avoid tag selectors, but if they must be used, I use a direct-child selector (
>) to ensure these styles don't leak where they're not wanted.
We've all experienced a CSS nightmare at some point; it takes discipline and conventions to keep everything sane. I've tried a lot of things, some worked, some didn't. In this article I've tried to distill 'the best bits'. Drop a comment if you've discovered techniques that help maintain your sanity.