Search

Pony Foo

Ramblings of a degenerate coder

CSS: The Good Parts

(13 comments)reading time: , published

I've decided to pour my thoughts of how the CSS of an application should be modelled into a formal style guide. I've been using this approach for over a year, and now I'm also moving to implement it on the job, as well.

You can find the latest version at css on GitHub. Pull requests, suggestions, ideas, and critique are all welcome!

.bc-style-guide {

The so-called "nico-style" brought to markdown. Because Style Guide.

This style guide is a jab at solving collisions between CSS class names, and issues that ultimately lead to confusion, having to use !important rules, copying and pasting style declarations, and other awful aspects of CSS developments.

Astonishignly, this style guide won't do anything for you that you're not able to figure out for yourself. That means you won't see the benefits unless you try and follow most of the conventions laid out next.

The recommendations found in this style guide are generally just guidelines and aren't set in stone. Even if they're refered to as "rules". Disregard them at your own discretion.

Feel free to fork this style guide, or better yet, send Pull Requests this way!

Namespaces

Components should always be assigned a unique namespace prefix.

  • The namespace can be as short as a single character, or as long as 5 characters
  • Where possible, the namespace should be a meaningful shorthand
  • In class names, the namespace must be followed by a single dash
  • Views should be treated as individual components

Consider the following example, where we assigned ddl to a drop down list component. Take note of the class names.

Good
.ddl-container {
  // ...
}

.ddl-item-list {
  // ...
}

.ddl-item {
  // ...
}
Bad
.item-list {
  // ...
}

.dropdown-item-list {
  // ...
}

.xyz-item-list {
  // ...
}

Classes that are meant to be shared among a large set of elements, or provide reusable styles, should be grouped under a universal namespace, such as uv.

Good
.uv-clearfix {
  // ...
}
Bad
.clearfix {
  // ...
}

See Nesting for information in regard to how styles should be overridden

Classes

Class names must follow a few rules.

  • Must be all-lowercase
  • Words must be separated by single dashes
  • As short as possible, but as long as necessary
    • Don't abbreviate words carelessly
  • Name things consistently
  • Meaningful description of the elements that should use it
  • Keep your non-prefix word count below 4
Good
.ddl-item {
  // ...
}

.ddl-selected {
  // ...
}

.ddl-item-selected {
  // ...
}
Bad
.ddlItem {
  // ...
}

.ddl-item-container-text {
  // ...
}

.ddl-foo-bar-baz {
  // ...
}

Attributes

Attributes make decent selectors from time to time. Some ground rules apply.

  • If the "exists" check suffices, use that
  • Don't overqualify using a tag name
Good
[href] {
  // ...
}
Bad
a[href] {
  // ...
}

[href^='http'] {
  // ...
}

id attribute

While the id attribute might be fine in HTML and JavaScript, it should be avoided entirely inside stylesheets. Few reasons.

Good
.ur-name {
  // ...
}
Bad
#ur-name {
  // ...
}

Just assign a class name to the element.

Tag Names

Tag names in selectors follow a few rules.

  • Application level styles that are only overridden in a few places are okay to use tag name selectors
  • Not semantic. Avoid where possible, use class names instead
  • Fine to use when there's a ton of elements under the same namespace that need a small tweak
  • Don't overqualify (a.foo)
Good
button {
  padding: 5px;
  margin-right: 3px;
}

.ddl-button {
  background-color: #f00;
}
Bad
.ddl-container button {
  background-color: #f00;
}

Selectors and Nesting

Styles shouldn't need to be nested more than three (four at worst) levels deep. This includes pseudo-selectors. If you find yourself going further, think about re-organizing your rules (either the specificity needed, or the layout of the nesting).

Good
.sg-title-icon:before {
  // ...
}

.dg-container .sg-title {
  font-size: 1.1em; // larger segment title inside dialogs
}
Bad
.dg-container .sg-container .sg-title {
  font-size: 1.1em;
}

.dg-container .sg-title span:before {
  // ...
}

If a component needs to be different within another component, these rules apply.

  • Where possible, give a class name using the parent namespace to the child component
  • If that's not possible, then use a nested selector

Suppose you have a User List component .ul-* and a User Card component .uc-*.

Good
<div class='ul-container'>
  <div class='uc-container'>
    <span class='uc-name ul-card-name'>John Doe</span>
  </div>
</div>
.ul-card-name {
  // ...
}
Okay
<div class='ul-container'>
  <div class='uc-container'>
    <span class='uc-name'>John Doe</span>
  </div>
</div>
.ul-container .uc-name {
  // ...
}
Bad
<div class='ul-container'>
  <div class='uc-container'>
    <span class='uc-name uc-name-in-ul'>John Doe</span>
  </div>
</div>
.uc-name-in-ul {
  // ...
}

Organization

Ideally, you should keep your stylesheets separated in different files. Either of the approaches below is fine. The former is prefered.

  • Use a single all.{styl,less,scss} file and have it @import every other file
  • Use a build tool to glob the styles directory

A few rules apply.

  • Each component should take up its own file
  • Styles applied globally to tag names, see Tag Names, should be kept in a single file
  • Where possible split presentation-specific styles from layout-specific styles. See below

Presentation-Specific vs Layout-Specific Styles

Presentation-Specific styles are those that only alter the visual design of the element, but don't change its dimensions or position in a meaningful way. The examples below are presentation-specific.

  • Rules such as color, font-weight, or font-variant
  • Rules that animate other properties
  • font-size is not considered a meaningful dimension change
  • padding may fit this category (loosely), but only if box-sizing: border-box; is in effect
  • max-width and max-height may fit either category, but it's generally reasonable to consider them presentation-specific

Layout-Specific Styles are those that change the dimensions or positioning of DOM elements. These are mostly layout-specific.

  • Rules such as margin or padding
  • width, and height
  • The element's position
  • z-index, definitely

Where possible, it's suggested to explicitly split styles into these two categories. The explicit differentiation could be made in a few different ways.

  • (bad) No differentiation
  • (decent) Layout-specific first, presentation-specific later
  • (good) A line-break between both categories
  • (better) Split in subsequent style declarations using the same selector
  • (best) Declaring the rules in different files altogether
Good
.foo {
  position: fixed;
  top: 8px;
  right: 8px;
  padding: 2px;
  font-weight: bold;
  background-color: #333;
  color: #f00;
}
.foo {
  position: fixed;
  top: 8px;
  right: 8px;
  padding: 2px;

  font-weight: bold;
  background-color: #333;
  color: #f00;
}
.foo {
  position: fixed;
  top: 8px;
  right: 8px;
  padding: 2px;
}

.foo {
  font-weight: bold;
  background-color: #333;
  color: #f00;
}
Bad
.foo {
  font-weight: bold;
  background-color: #333;
  color: #f00;
  position: fixed;
  top: 8px;
  right: 8px;
  padding: 2px;
}

.foo {
  right: 8px;
  color: #f00;
  padding: 2px;
  top: 8px;
  background-color: #333;
  font-weight: bold;
  position: fixed;
}

Styles

These rules apply to your CSS property values

  • If the value of a property is 0, do not specify units
  • The !important rule should be aggressively avoided.
    • Keep style rules in a sensible order
    • Compose styles to dissipate the need for an !important rule
    • Fine to use in limited cases
      • Overlays
      • Declarations of the display: none !important; type
  • Keep z-index levels in variables in a single file. Avoids confusion about what level should be given to an element, and arbitrarily-high `999-style values
  • Use hex color codes #000 unless there's an explicit need for an rgba declaration
  • Dislike magic numbers
  • Avoid mixing units
  • Unit-less line-height is preferred because it does not inherit a percentage value of its parent element, but instead is based on a multiplier of the font-size.
Good
.btn-red {
  color: #f00
}

.btn {
  color: #222;
}
Bad
.btn {
  color: #222;
}

.btn-red {
  color: #f00 !important;
}

Media Queries

If you are reading this, I salute you. You're almost as boring as I am. I'm more boring because I actually wrote the damn thing. It's not a contest, though.

A few rules apply to media queries.

  • Settle for a few (2-3) breakpoints and use those only
  • Don't wrap entire stylesheets in media queries
  • Instead, modularize media queries wherever possible, keep them relevant to the components
  • Approach your styles in a Mobile First manner. Generally you add more things as you get more real state. Mobile First logically follows
Good
.co-field {
  width: 120px;
}

@media only screen and (min-width: 768px) {
  .co-field {
    width: 400px;
    color: #f00;
  }
}
Bad
.co-field {
  width: 400px;
  color: #f00;
}

@media only screen and (max-width: 768px) {
  .co-field {
    width: 120px;
    color: initial;
  }
}

Frameworks and Vendor Styles

You should shy away from all of these. A few rules apply.

  • Stay away from frameworks
  • Use normalize.css if you want
  • Vendor styles, such as those required by external components are okay, and they should come before you define any of your own styles

Languages

Some rules apply to stylesheet, regardless of language.

  • Use a pre-processor language where possible
  • Use soft-tabs with a two space indent
  • One line per selector
  • One (or more) line(s) per property declaration
    • Long, comma-separated property values (such as collections of gradients or shadows) can be arranged across multiple lines in an effort to improve readability and produce more useful diffs.
  • Comments that refer to selector blocks should be on a separate line immediately before the block to which they refer
  • Use a plugin such as TrailingSpaces in Sublime Text to get rid of trailing spaces
Good
.foo {
  color: #f00;
}

.foo,
.bar,
.baz {
  color: #f00;
}
Bad
.foo {
    color: #foo;
}

.foo, .bar, .baz {
  color: #f00;
}

.foo {
  color: red;
}

Not Stylus

These rules apply to every language except Stylus.

  • Use a single space after the colon
  • Always end property declarations with a semicolon
  • Put spaces after : in property declarations
  • Put spaces before { in rule declarations
Good
.foo {
  color: #f00;
}
Bad
.foo{
  color: #foo;
}

.foo {
  color:#f00;
}

.foo {
  color: #f00
}

Not CSS

Rules applicable to most pre-processor languages.

  • Put comments in // statements
  • Prefer nested selectors .foo { .bar {} } vs .foo .bar {}
    • Only if both .foo and .foo .bar need styling
  • Use &{selector} to concatenate selectors
  • Don't blindly over-nest
  • Keep z-index levels in a single file, using variables
Good
// foo
.bar {
  // ...

  .baz {
    ///...
  }
}
Bad
/* foo */
.bar {
  // ...
}

.bar .baz {
  // ...
}

Stylus

Rules specific to Stylus.

  • Omit brackets {} in rule declarations
  • Omit : and ; in property declarations
  • Use transparent mix-ins
  • Use nib
Good (Stylus)
// foo
.foo
  color #f00
.foo
  color #f00

  .bar
    padding 2px
Bad (Stylus)
/* foo */
.foo {
  color: #f00;
}
.foo {
  color: #f00;
}

.foo .bar {
  padding: 2px;
}

License

MIT

Fork away!

}

Comments(13)

phil

I think there's a lot of good ideas here

The only area I'm uncertain about is the language style rules. If your goal is to have this be a universal guide, I feel like style guidelines don't belong. (For example, I strongly feel that smart tabs are the only correct indentation. ;-)

Instead, much like separating concerns into multiple CSS files, maybe it makes sense to split the document into Universal Rules (pretty much everything above Languages) and Project- or Company-Specific Rules.

Nicolas Bevacqua

That makes a lot of sense. I might do that separation, or at least explain that consistency is what matters in that case, not so much how many spaces, or what type.

Thanks for the suggestion! You're welcome to send in a pull request.

Sylvain Pollet-Villard

I disagree with many of the points in this article, mainly because of the examples declared "Good" or "Bad" without any context. Here are my comments detailed by sections (please apologize for any misspellings as English is not my mother tongue)

Namespacing

I do not use prefixes but instead nested classes:

.ddl .container
.ddl .item-list
.ddl .item

There are many reasons to do that :

  • you have less class names to keep in mind
  • you can share properties between common parts of different components, for example padding of all item-lists of your website
  • special style rules for components can be applied by overriding them with the component class. Overriding instead of duplicating means smaller files
  • it prevents you to have confusing things like a styled .ddl-item-list without a .ddl-container parent : visually right but semantically wrong

A universal namespace is pointless in my opinion. Namespacing aims to separate spaces, and universal means "all spaces".

Attributes

Why [href] should be considered good and a[href] bad ? Stylesheet rules apply to elements, developers have in mind elements when styling, not attributes. It is difficult to remember all the elements that have a href property, not to mention this list may change over time. There are very very few cases when attributes are the most appropriate selectors and specifying a tag name is not overqualifying but legitimate caution.

id attribute

I got the feeling that banning IDs was more important to you than banning !important; If this it the case, I totally disagree. IDs are not a priority nightmare, actually its one of the simplest properties of CSS specificity : IDs > classes > tag names. If you are totally sure that the element you are working with is unique in the page (i.e. #main), then an ID is fine.

Tag names

Tag names are definitely semantic ! That is what HTML is made of and has been thought to describe entire documents. This is a general observation that developers do not like HTML and often use a <div> soup with lots of IDs, class and names instead of picking the appropriate elements. That is a shame because browser stylesheets & behaviours use intelligently this variety of elements (think about <input type="email"> instead of <input type="text" class="input-mail">). So there is no valid reason why .ddl-container button should be a bad selector.

Selectors and Nesting

Setting an arbitrary number of three-four levels deep is meaningless without any context. Some complex cases require more levels while the DOM is totally correct and has no reason to change. A simple rule to adopt is to systematically use the selector with the minimum suitable selectivity.

Again, the examples placed in BAD do not deserve this title, also they do not have more than three levels nesting.

Nicolas Bevacqua

It's a style guide. The "bad" examples aren't (all) inherently bad, they just don't conform to the style guide.

Sylvain Pollet-Villard

It's more than a style guide if you make assumptions on what is the right level of nesting, how many media queries we should have, what CSS units we should use, or just claim "Stay away of frameworks". I guess "good" means "ideally" and "bad" means "fine", but it would have been interesting to go deeper into the different use cases.

Shankar Cabus

"I do not use prefixes but instead nested classes", don't make this! Avoid more than one/two nesting level.

Much that he said is already established in the community and with their justifications. Take a look:

I also disagree with another things. Just as I disagree with the SMACSS, BEM, OOCSS and other style guides. Style guide is not a rule, you use as you want and if you want. The important thing is to bring together the best practices of each SG and create what is better for you.

Sylvain Pollet-Villard

"I do not use prefixes but instead nested classes", don't make this! Avoid more than one/two nesting level.

In the example I gave, it is one level deep, right ? The other guides you are referring to say "avoid unnecessary nesting" and that's what I do ! The justification given is that it can bring too much specificity to the selector, I totally agree with that. In case you missed what I wrote in bold : systematically use the selector with the minimum suitable selectivity

I think that, for this example, there is a legitimate use of nested classes and I gave four reasons why. Semantically, .ddl-item and .ddl .item have the same specificity. Replacing a nesting level by a prefix does not reduce the specificity. Instead, it makes things more complicated to step back :

.item {
    /* basic rules */
}

.ddl .item {
    /* extending/overriding rules for DDL component */
}

<ul class="ddl container">
   <li class="item">

compared to :

.item {
    /* basic rules */
}

.ddl-item {
    /* copy again the basic rules
      then completing/modifying with specificities of DDL component
      no factorisation/inheritance possible with .item class */
}

<ul class="ddl-container">
   <li class="ddl-item">

If I decide to add generic item classes:

.item {
    /* basic rules */
}

.ddl-item {
    /* same specificity than .item rules, I need to ensure that these rules
       come after so they override .item rules and not the opposite */
}

<ul class="ddl-container container">
   <li class="ddl-item item">

This is what nesting is designed for, and through it is true that some developers tend to abuse it unnecessarily, that does not mean that prefixes solve this issue or that we should fix a maximum level of nesting to be respected in all circumstances.

Sylvain Pollet-Villard

Presentation-Specific vs Layout-Specific Styles

I tried this approach on my last project with a layout.less, fonts.less and colors.less files. I regretted this choice afterwards, as many selectors were duplicated and I got to handle too many different files (each component is divided into three LESS files, it quickly becomes unmanageable). However, in another project I just separated the graphic chart (theme.less) from the rest of my files. Inside, I declared all the variables needed for presentation-specific (call it theme) rules, and I found this approach much less painful.

Note that you should not separate font-size and margin/padding rules in different files, especially when working with font-relative units like em

Styles

Use hex color codes #000 unless there's an explicit need for an rgba declaration

I think many agree that RGB notation is more representative than hexadecimal. If you minify your CSS afterwards, it will convert RGB to # so why bother ?

Avoid mixing units

No. Percentages, font-relative units and pixels all have a purpose. For example, em/rem are (often) suitable for paddings, percentages (often) for layouts dimensions and pixels (often) for min/max rules, image sizes and media query breakpoints. For each rule, there is an appropriate unit depending on what you want. Changing dynamically the font-size and the zoom setting of your browser is a good way to understand the importance of choosing the right units.

Media Queries

Settle for a few (2-3) breakpoints and use those only

Whaaat ? There are millions on resolutions out there and 2-3 drawers are sufficient to cram all of them ? Responsive Web Design was not designed to chop the web into 2-3 pieces. We have to make sure that the page looks good without any media query applied, then we can declare as many media queries as needed to enhance user experience on specific device classes as long as they are documented and tested.

Stay away from frameworks

No argument, no example ? You have tested all existing and future frameworks to claim that? Rather than dismiss the problem to "frameworks", it would be more relevant to explain why some frameworks are doing wrong.

bradley.m.wheel

I'm curious as to why you say stay away from frameworks? What's been your experience with Foundation and Bootstrap and what is it about them that we should stay away from?

Nicolas Bevacqua

Bootstrap is awesome for quickly hacking together a presentation layer. This is particularly neat when putting together back-end, admin panelsy views that won't be customer-facing.

It's not so hot when developing customer-facing products because they end up looking the same, mostly. My other issue with CSS frameworks is that they get very much in the way of the namespacing I suggest, which is at the core of the style guide

Joseph Zimmerman

In your "Bad" example for Media Queries, shouldn't it be max-width instead of min-width?

Nicolas Bevacqua

Absolutely! Mobile First for the win!

marcelvuijk

Very cool article! I`m wondering though, when you write about media queries you say it is better to keep it per element, so no entire stylesheet for media queries. I miss more info on the why. Because, I do not really see the advantage of this and it is much less readable / maintainable right?

Also something a nice thing I found out: in wordpress you can use wp_is_mobile() to check wether a visitor is mobile. At first, if I did not want to show a slider on my website for mobile I would just add a display:none rule to the element for mobile CSS. Now I have found out that it is way better to actually render parts different for your mobile website, like only rendering a slider or sidebar if visitor is not on mobile.

Furhtermore, great and usefulle article. Very proud on this website template I made recently, thanks to articles like yours: the website