Recently Chris Koyera answered questions of readers of Smashing Magazine. One of the
questions was how to recognize CSS code with a “nice”:
How can you tell what your CSS is doing? What signs indicate that the code is not optimal, or that the developer wrote it carelessly? What are you looking at first to determine if the code is bad or good?
I thought that I could expand and complement Chris's answer based on my own experience.
I work at
BSkyB . I have been making great sites - I have been working on the last of them for over a year now. Bad CSS code gives me a lot of problems. When you have been involved in a single site for months, you simply cannot afford bad code, and it must be corrected.
I want to share a few things that I pay attention to first and foremost in order to make an impression about the quality, maintainability and purity of the CSS code.
Cancel styles
Any CSS rules that override previously set styles (except when a style is reset) are an alarm bell. Cascading style sheets, by definition, must inherit from previous definitions and supplement them, not cancel them.
')
Any definition like:
border-bottom:none; padding:0; float:none; margin-left:0;
usually does not mean anything good. If you have to reset the
border
, then most likely you installed it too early. It is difficult to explain, therefore I will give an example:
h2{ font-size:2em; margin-bottom:0.5em; padding-bottom:0.5em; border-bottom:1px solid #ccc; }
Here we set the
h2
elements not only the font size and indents, but also margins and underscores to visually separate the title from the rest of the content. But it is very possible that in another place we will not need either fields or underscores. Perhaps we will write something like:
h2{ font-size:2em; margin-bottom:0.5em; padding-bottom:0.5em; border-bottom:1px solid #ccc; } .no-border{ padding-bottom:0; border-bottom:none; }
Now we have 10 lines of code and the ugly class name. It would be much better to do this:
h2{ font-size:2em; margin-bottom:0.5em; } .headline{ padding-bottom:0.5em; border-bottom:1px solid #ccc; }
8 lines, no cancellation of styles and a beautiful, meaningful class name.
Moving down the stylesheet, add rules, not subtract. If you have to cancel previously installed styles, most likely you added them too soon.
Imagine a CSS file for ten thousand lines with similar style cancellations. A lot of extra code! Gradually add new definitions over old, simpler ones, do not start building too complex rules too early, otherwise you will write a lot more code, and it will be much less to do.
When I see that a rule overrides the previous one, I almost certainly know that the styles are organized incorrectly and need to be reworked.
Magic numbers
I especially hate them! A magic number is a meaningless value that is used because it “just works.” For example:
.site-nav{ } .site-nav > li:hover .dropdown{ position:absolute; top:37px; left:0; }
top:37px;
- this is a magic number. The only reason it is here is that the list items are 37 pixels high, and the drop-down submenus should appear at the bottom of the menu item.
The problem is that these 37 pixels are pure coincidence, and this constant cannot be relied on at all. What if someone changes the font size in the menu item, and it will have 29, not 37 pixels in height? What if in Chrome the menu item will be 37 pixels high, and in IE - 36? This number only works in one particular case.
Never, never use values that "just work." In the previous example it would be much better to write
top:100%;
instead of
top:37px;
And this is not the only problem with magic numbers. In addition to insecurity, they also create a communication problem. How can another developer understand where this number came from? If your code is bigger and more complicated than the above example, and some of the magic numbers suddenly stopped working, you will encounter the fact that:
- another developer, not knowing where this number came from, will be forced to write the correct style for this case from scratch;
- or, if he is very careful, he will leave the number in place and try to solve the problem without touching it. Thus a curve and an ugly crutch risks remaining in the code forever and acquiring new crutches.
Magic numbers are bad. They quickly become obsolete, they interfere with other developers, they cannot be explained and they cannot be relied upon.
There is nothing worse than to stumble upon such an inexplicable number in someone else's code. Why is it here? What does it mean? Can I touch it or not? I ask these questions whenever I see such a number. And the most important question: “How can one achieve the same result without magic?”
Run from the magic numbers like the plague!
Overly narrow selectors
Something like this:
ul.nav{} a.button{} div.header{}
These are selectors to which absolutely superfluous specifications are added. They are bad because:
- they are almost impossible to reuse;
- they increase the specificity;
- performance suffers from them.
In fact, they can (and should) be written as:
.nav{} .button{} .header{}
Now you can apply
.nav
to
ol
,
.button
to
input
and quickly replace the
div
with the class
.header
with the
header
element when we align the site with HTML5.
Although browser performance suffers from such selectors not very much, it still suffers. Why force it to
.button
over all elements of
a
in the search for a class
.button
, if you can limit yourself to just one class? Here are more extreme examples:
ul.nav li.active a{} div.header a.logo img{} .content ul.features a.butto
All of them can be greatly reduced or rewritten:
.nav .active a{} .logo > img {} .features-button{}
Every time I stumble upon too detailed selectors, I try to figure out why they are set that way, and whether it’s possible to reduce them.
Hard-coded absolute values
Just like magic numbers, they do not promise anything good. Here is an example:
h1{ font-size:24px; line-height:32px; }
It would be much better to write:
line-height:1.333;
line-height:1.333;
Interlinking is always better to be set relatively, so that the code is more flexible. When you change the font size, it will change automatically. And if you specify it in pixels, then you have to write something like this:
h1{ font-size:24px; line-height:32px; } .site-title{ font-size:36px; line-height:48px; }
And so every time the font size of the header changes. So much better:
h1{ font-size:24px; line-height:1.333; } .site-title{ font-size:36px; }
It seems that the difference is not big, but in a large project it can make a big difference.
By the way, this applies not only to
line-height
. Almost any absolute value rigidly written into the code should be suspicious.
The only case when it really makes sense to hard-code an absolute value is in the case of working with things that should always be of a certain size, for example, sprites.
Brute force
This is one of the extreme particular cases of hard-coded magic numbers and some other techniques that are used to make the layout work:
.foo{ margin-left:-3px; position:relative; z-index:99999; height:59px; float:left; }
This is a terrible style! All these ugly rules are fancy for the sole purpose - to push an element to the right place at any cost. Such code speaks either of a very poorly designed layout, or of a lack of understanding of how the CSS block model works, or of both.
If you are well thought out layout and versed in the block model, you are unlikely to have to use brute force. If I see such code, I immediately try to figure out what the problem is, and whether I need to go back a few steps to get rid of the need to write such crutches.
Dangerous selectors
By dangerous selectors, I understand those that are much wider than necessary. Here is the simplest and most obvious example of such a selector:
div{ background-color:#ffc; padding:1em; }
Why, why cover every
div
on the page of this carpet bombing? Why would anyone need a selector like
aside{}
? Or
header{}
, or
ul{}
? These selectors are much, much wider than necessary, and lead to the fact that we have to cancel the styles, as we have already said.
Let's look at the example with
header{}
more detail. Many use this element to create a page header, which is completely correct. But if you write styles for it like this:
header{ padding:1em; background-color:#BADA55; color:#fff; margin-bottom:20px; }
then it is not so right. The
header
element does not necessarily imply the
header
entire page, it can be used several times in different contexts. It is much better to use a class, for example
.site-header{}
.
Defining such detailed styles for such a common selector is very dangerous. They will leak into completely unpredictable places as soon as you begin to reuse this element.
Make sure your selectors
hit you right on target .
Here is another example:
ul{ font-weight:bold; } header .media{ float:left; }
When I see how styles are applied to an element or to a strongly generalized class, as in this example, I start to panic. I know that they are too universal and I will have problems. These styles can be inherited in places where it will be completely undesirable, and I will have to cancel them.
Reactive use! Important
You can use
!important
. And this is a really
important tool. However, it is worth using it wisely.
!important
must be used proactively, not reactively.
This means that you can use it when you are absolutely sure that you will always need this style to have priority, and you know about it beforehand.
For example, you are sure that you always want to see errors in red:
.error-text{ color:#c00!important; }
Even if an error message is displayed inside a block in which the text color is blue, we can be sure that the error will remain red. We always want to report an error in red, and therefore we immediately write
!important
.
But when we use
!important
reactively, that is, in response to a problem that has arisen, when we are confused and instead of sorting it straight ahead, then this is bad.
Reactive use
!important
does not solve the problem, but only hides it. It is necessary to treat the disease, not the symptoms. The problem has not gone away, we just blocked it with a super-specific selector, while it was necessary to do refactoring and architecture.
ID
This kind of "bad smell" is very important when working in a large team. I already wrote that
id is bad because they greatly increase the specificity of selectors. They are no use, and they should never be used in CSS. Use them to link HTML elements with JavaScript code, but not to define their style.
The reasons for this are simple:
- id can be used on the page only once;
- class can be used as much as you want;
- most of the rules applied to id can be scattered across several classes;
- id is 255 times more specific than a class ;
- This means that you will need to apply 256 classes to the element in order to outweigh one id.
If this is not enough for you, then I don’t know what else to say ...
If I see an id, I immediately try to replace it with a class. Excessive specificity of selectors ruins large projects, so it is vital to keep it as low as possible.
Finally - a little exercise. Try to elegantly solve
this problem . Hint:
so - not elegant; and
so - too.
Vague class names
A vague name is one that does not specifically describe the purpose of a class. Imagine a class
.card
. What is he doing?
This is a very vague name, and vague names are bad due to two main reasons:
- you cannot guess what it means;
- it is so general that it can easily be accidentally overridden by another developer.
The first point is very simple: what does
.card
mean? What style does he set? Task cards in project management system? Playing card in an online casino? Credit card image? It is difficult to say, because the name is too vague. Suppose we mean a credit card. Then it is much better to name the class
.redit-card-image{}
. Yes, much longer, but much, much better!
The second problem with vague names is that they are very easy to override by accident. Suppose you are working on an online store site. You use the class
.card
, meaning the number of the credit
.card
with the account. And another developer at this time adds the opportunity to buy a gift and attach a greeting card to it. And he also calls the class
.card
, and writes his own rules for him, which conflict with yours.
This can easily be avoided by using more accurate class names. Classes like
.card
or
.user
too vague. They are uninformative and easy to override. Class names must be as accurate as possible.
Conclusion
So, we looked at a few examples of the code "with a bite." These are things that must always be remembered and avoided with all their might, or rather, only a small part of them, in fact, there are many more. When working on a large project that lasts months and years, it is vital to keep the code in good shape.
Of course, there are exceptions to every rule, but they must be approached individually. In most cases, such a code should be carefully avoided.