For the past two weeks I’ve been talking about css. How we might organize css files differently and whether or not some of our long held best practices aren’t as best as we thought. We might be able to improve things by reaching a little further into the development world and learning to make better use of abstraction in our design process.
What Is Abstraction?
“Abstraction is the ability to define a new concept in terms of other, more simple concepts,” in the words of Chris Eppstein, who’s post, Why Stylesheet Abstraction Matters, is one I’ll be leaning heavily on in writing this post.
Abstraction attempts to factor out implementation details so you can focus on fewer concepts at one time. I suspect most of you have used a php include statement at one time or another. It’s a simple example of abstraction. You want to include the same set of code in the header to each page on a site. No need to reinvent that code over and over. Write it once, place it in its own file, and include that file wherever you need it.
The principle of DRY (don’t repeat yourself) calls for abstraction.
Other common abstractions you might already use:
- css classes
- WordPress template_tags
- php functions
- grid frameworks like 960 and Blueprint
- libraries like jQuery
Let’s look at the first. A css class is an abstraction. You define a set of styles that can be reused anywhere in your html. Someone working in the html alone doesn’t need to see all the details of the class. They just need to know that by adding class=”frame” to any html element, they get a consistent frame style applied.
Abstractions make the complex easier to work with. Take jQuery. Look at the explosion of Javascript solutions since its introduction. jQuery is a lot easier to learn and work with than Javascript itself. It’s easier because it hides low level details you don’t really need to know, like how to make an Ajax request. You just want to make the request and get a response. How it all happens is probably less important to you.
Some Pros and Cons of Abstraction
Abstraction isn’t a perfect solution. It has a few downfalls, thought the pros generally outweigh the cons. Here are some of those pros and cons.
Pros:
- Compatibility — The hidden implementation can be made to work across browsers, devices, etc.
- Ease of maintenance — The code is maintained in one place
- Productivity — It’s quicker to use the abstraction than reinvent it’s details
- Reuse of code — Aids DRY principles
- Refactoring — With implementation in one place it’s easier to improve the code
- Organizational clarity — Less visible code is easier to read and follow
- Reduces errors — With code is in one place errors aren’t repeated across a project
Cons:
- Additional learning curve — The abstractions need to be learned
- Loss of knowledge about what’s being abstracted — Learning the abstraction doesn’t necessarily mean understanding the underlying language
- Finding code — Sometimes it’s not so easy to find where the implementation details are located
- Fixing code — If you don’t understand the underlying language it could be difficult to correct low level errors
How Can We Abstract CSS?
I’ve already mentioned a couple of ways, classes and frameworks. A class creates a reusable set of styles that can be applied anywhere in your html. The person applying the class doesn’t need to know the details of how the background-color is set.
{code type=css}
.box {
background-color: green;
background-color: #0f0;
background-color: rgb(0,255,0;
}
{/code}
They just need to know it’s visually green. Instead of having to think about the implementation details they can work at a higher layer in the design.
Creating reusable classes is at the heart of both OOCSS and SMACSS. These classes can be chained to create even more flexibility and abstraction.
Frameworks might be collection of different classes you apply to create something more complex like a grid. They might also be a collection of styles applied directly to html elements to create something consistent and maintainable.
CSS is a relatively simple language to learn. Once you understand how to use selectors and properties and gain some additional understanding of how precedence works, you’ve got much of the language. That simplicity is sometimes limiting though and creates complexity for the designer/developer. It’s easy enough to understand that floating something to the left does indeed float it to the left. What’s harder to do is understand how to use floats to build a fluid 3 column layout.
The simplicity of css also leads us to repeatition. You’re either going to specify the same color over and over again or repeat the same series of selectors in multiple places, once to define a color, another to define a width and so on. Worse though is when you want to change a color used many times. Search and replace is about our only option other than manually finding every occurrence that needs changing.
Variables could easily fix the above. Wouldn’t it be better to define !link_color once at the top of your stylesheet and then apply color: !link_color; in the rest of the file. In terms of abstraction the variable becomes a new higher level concept that’s defined by something simple, a value. The abstraction is easier to work with.
Even with something as simple as variables, there are those who suggest they should not be added to css.
Their arguments don’t appear to have stopped others from pushing toward a css with variables.
Functions are familiar abstractions to anyone who works with a programming language. The implementation of the function is written once and then the function is called whenever we need that implementation. CSS already has some built in functions like rgba() and url(). What’s mainly absent is an ability to define new functions.
Expressions create new values by combining values, variables, operators, and functions. If you’re working with elastic grids you’re likely familiar with the expression:
result = target ÷ context
which helps us turn a fixed grid into a flexible one. CSS expressions could give us a way to define some things like this once in terms of variables and place the result into a new reusable variable. It’s a way to build up higher level variables from lower level variables.
Mixins are the contents of a selector (the properties and values) without the selector itself. They sit in for larger blocks of css and can be passed variables to control behavior. For example
{code type=css}
=border-radius(!border-radius)
:border-radius: !border-radius
:-webkit-border-radius: !border-radius
:-moz-border-radius: !border-radius
: -o-border-radius: !border-radius
:-ms-border-radius: !border-radius
:-khtml-border-radius: !border-radius
{/code}
is a Sass mixin for using border-radius cross browser. Using the mixin whenever you want to add a border-radius to an element you would add
{code type=css}
div
+border-radius(0.5em)
{/code}
Much simpler than having to write out all the vendor prefixes every time. Just as nice is that as browsers drop the prefix in favor of the standard you only need to make the change once, in the mixin.
One last abstraction I’ll mention is Macro Expansion, which generates selectors and styles through variables, loops, and conditionals. I’ll save the details for when I talk about Sass, but the general idea will be familiar if you’ve worked with any programming language.
Summary
As currently exists css is a pretty simple language to learn and use. It’s simplicity is a benefit to helping more people learn to use the language, but that same simplicity can be limiting in what it enables you to do.
Abstraction is a way to use simple concepts as building blocks to create new and higher level concepts. They hide implementation details and allow you to focus on using the abstraction instead of having to rebuild it time and again. Abstractions offer a variety of benefits, though not without some cons thrown into the mix.
CSS already gives us one abstraction in the form of reusable classes and OOCSS and SMACSS take classes to a new level. Developers have built additional css abstractions in the form of frameworks that combine classes or groups of selectors to make site development quicker and easier.
Unfortunately some fundamental abstractions like variables and functions are missing. Fortunately these missing abstractions exist in css preprocessors like Sass and Less.
Have you started using OOCSS or SMACSS? How about Sass or Less? Any thoughts on how abstractions have helped you design and develop better sites?
Download a free sample from my book, Design Fundamentals.
Along the same vein, I’ve found I can avoid lots of classes simply by using careful selectors (e.g., input[type=”text”]). In one of my more simple-minded websites, I have no classes or ids whatsoever. Do you see any pros or cons in this approach?
I do, but I hope I can momentarily pass on the question since it’ll be covered in next Monday’s post on OOCSS. It’s basically that you’re tying the style to that one particular input, which limits the reuse of those styles.
It’s less a matter of being able to avoid classes or avoid certain types of selectors as it is a choice in which approach is better.
The biggest con of abstractions is that they tend to be “leaky.” For instance, it’s really easy to over-declare styles when using mixins.
Even if preprocessors allow you abide by DRY, the CSS they generate may not stop the browser from repeating itself unnecessarily.
In the same way that you can do some real damage by learning jQuery without knowing the underlying JavaScript, it’s important to know how CSS selectors work [right to left] and how styles are inherited in the cascade. Without these fundamentals, preprocessors can lead to really bad stylesheets.
Interesting Brad. Are there any articles you can point me to? It will probably be a topic I should cover when talking about css preprocessors in a few weeks.
I hear you about not understanding the underlying language. I do think that’s one potential con of abstraction. It’s not an automatic bad thing, but there is potential for badness.
For example my computer only speaks in 1s and 0s, but that’s not how I communicate with it. For the most part we communicate well and more more efficiently than if I did have to speak 1s and 0s only. On the other hand every so often a program crashes and perhaps crashes often and all the computer can tell me about why is a series of 1s and 0s that I don’t understand. when that happens the abstraction hasn’t really helped me, however it helps in so many other ways that the net is still much more a pro than a con.