Don’t start coding at the abstract level. Arriving at the right abstractions is a process that requires multiple passes - also known as refactorings. What you should do, is start solving the problems at hand - we call these “implementations” - and then gradually identify redundancies. Redundancies have the potential to become reusable abstractions. We call these “patterns”.
They’ve done a great job identifying and implementing some common layout patterns and making them easy to reuse.
But to this day, I have not for the life of me been able to actually put these collections of abstractions to work in real projects.
I’ve tried. I’ve really wanted to make it work. But somehow it always felt like the same things that made it so easy to get started would soon evolve into obstacles.
It felt like the projects - as they matured - no longer fit with the patterns of the framework. The quickfix of bootstrapping the project would always seem to turn against me as the project progressed.
The nature of abstraction
If you stop to think about it, this should come as no surprise, really. Beginning an implementation with a set of abstractions is actually a backwards and detached approach.
An abstraction is a set of general properties shared by multiple concrete phenomena. In a real world project, we start out by having nothing but concrete phenomena. Deriving a set of useful abstractions should be a process going from the concrete towards the abstract level. Not the other way around.
We make abstractions in code to save time. If a piece of functionality is used multiple times, it’s easier maintained if it exists in only one place. Also, it gets a lot easier to test if the piece of functionality works as expected.
Abstractions should emerge. Think of it as a process of evolution, the driving force behind which is refactoring - rewriting code to get rid of duplications and improve maintainability. When working with a body of code, you should continuously clean it up and improve upon it. Code is a living material, so be the good gardener and treat it with care. And yes people: CSS is code. :)
A process to make it happen
Some time ago, I did a presentation in which I introduced the concept of test-driven development in a CSS context.
The basic idea of test-driven development (TDD) is that you define a problem by writing a test - and then you fix it by writing code that makes the test pass.
That way you will get a system that’s covered and documented by tests. This makes it risk-free for you to rewrite your code at any time if you feel like something could be improved or abstracted into reusable components. If the test still passes, you know instantly that you haven’t broken any functionality.
The CSS variant of TDD uses online style guides instead of unit tests. Other than that, the concept and benefits are the same.
So, taking all of this into account, we should be able to grow our own CSS “framework” by starting at the concrete level.
What I’ve come to do lately, is start out all projects with a simple folder structure that outlines the process:
That’s it. That’s my framework of choice. Three empty folders and a .scss file to bring it all together. The style.scss contains only @import statements to bring in the contents of the files that will be added to the project.
Oh, note that I depend heavily on SASS to make this happen.
All work begins in the Implementation folder. A concrete implementation should be a a piece of the interface that can be styled in isolation and given a name. Examples of concrete implementations could be a header bar, a news list, a search box or something else, immediately tangible like that.
I’ll then create a .scss-file in the Implementation folder called - for instance - _news-list.scss. Don’t think too much about this name. Call it what it is. The whole point of this method is to not spend a lot of time up front trying to figure out the inner meaning of this thing that seems to be a news list. It’s a waste of time at this point to be guessing at what it might become down the line. Right now, it’s just a place to get started.
I actually can’t stress this point enough! CSS/semantic HTML-geeks like myself can get bogged down totally just thinking away, trying to arrive at the right name for something. We’ve been taught that it’s somehow wrong or dirty for us to just name the damn thing after what it seems to be right now.
Then, I create the news list as an item in my styleguide and style it according to the design.
And after that, I’ll pick a few more items, name them, add them to the styleguide and style them.
By now, I’ll most likely have started to spot duplications of basic properties. Specific colors are used throughout the design. I’ll start assigning those to variables and giving some thought to good names. Maybe it’s obvious right away that the red color is the “$brand-color” or the “$primary-color”, but it’s also perfectly okay to just call it “$red”. Maybe there’s no deeper meaning to it than it just being the tone of red that we use for this design.
Colors aside, I might also have stumbled upon actual visual patterns that appear across implementations. Maybe there’s always a certain composition to the headline structure of elements. Maybe there’s a certain type of grid layout that’s used to structure the contents of elements. And it’s certainly possible that the “news-list” was not the only kind of “-list”. So, by having now implemented a few of its’ cousins, I’ll have a better chance of picking a proper name and scope for the pattern that they share. I’ll probably also have talked to the art director or UX designers about why these elements look similar. I might have talked to an information architect or the client about the nature of this content or the terms that editors or domain experts use when communicating about it.
As duplications and patterns like these emerge among the implemented styles, I’ll start extracting them and placing the specific CSS rules into files inside the Patterns folder.
This is how the SCSS file structure will look after I’ve extracted some duplicates into a general “_list” pattern:
Notice how - apart from colors now being asigned to variables - some of the style rules from the first implementation of the _news-list have been moved into the “_list” pattern for reuse.
At first it might be difficult to identify patterns. I’ve found that there’s basically three angles to slice along:
- Obvious DRY violations - like in the above example.
- Concepts derived from the business object (if editors call the news blocks ‘teasers’, it’s neat to name the pattern accordingly).
- Inspiration from other frameworks or common best practices. (Something like the nav abstraction for instance might be perfectly useful in your context. If so, feel free to bring it on board).
It’s important to stress that this part of the process is not a discrete step. It’s something that happens continuously. It’s perfectly normal if the first few patterns turn out to be “wrong” and will have to drop back down into the concrete implementations again.
Likewise, it’s also okay to start seeing patterns among the patterns. In a matured project there’ll be a number of “stratos” of abstraction. These may have different levels of complexity or serve different purposes. One could be at the level of actual UI elements that just exist in different concrete flavours. Another could be at the level of general handling of spacing or typography within the layout. Hacks and special techniques such as a clearfix or the webkit hardware acceleration trigger also fit nicely into the Patterns category.
As these layers crystallize, it’s a good idea to create subfolders inside the Patterns folder to support orthogonality.
Finally, the Foundation folder is used only for importing and configuring libraries, webfonts and common variables such as grid settings. No custom styles should be put in the Foundation folder.
During the project, you should now be able to massage your code without hesitation, because it’s built on the foundation of your styleguide. This way, you can always tell right away, if a certain refactoring has broken something. That means, there’s no excuse for not trying to achieve a beautiful code base.