8 Best Practices for Perfect CSS Documentation

In the world of CSS, documentation is underused. Since documentation is not visible to the end user, its value is often overlooked by clients. Also, if it’s your first time documenting code, it can be difficult to determine what to document and how to do it most effectively.

Yet documenting CSS can offer a lot to your project: from encouraging better code practices to easing the onboarding of new team members. In this article I will explain the advantages of documenting CSS and share what my team and I at Bitovi consider to be the best practices to make documentation work for you–not the other way around. Let’s dive into it.

1. Set the Ground Rules

It’s hard to get on the documentation bandwagon when it’s not clear to you and your team what to document or how you want it to work. So the first step is to agree on which conventions you’ll use and how you should implement them. You can do this in a live document so everybody on the team can contribute. In this way, as your approach changes or becomes more comprehensive, you can keep it up-to-date. A shared Google doc, a wiki page on your code repo, or (even better) a page on your “living style guide” are all great places for this.

Now let’s looks at the actual “ground rules” that you can include.

2. Explain the Structure of Your Code Base

Understanding how your code is organized allows anybody to jump straight into the action from day one. A simple way of doing this is creating a map of your file structure where you can explain what’s in it and what should go where. When doing this pay special attention to those places where there could be ambiguity. For instance, indicating that the file “buttons.css” contains styles for buttons is not very helpful, but indicating that the “custom” directory is where custom styles for the theme are located can be a time saver.

Here’s an example:

Project Root
└── srs
    ├── styles               // Base styles. Styles placed here should be available anywhere in the application     
        ├── bootstrap-custom // Custom styles that overwrite bootstrap
        ├── custom           // Custom styles created for the application
        ├── demos            // Demos to illustrate styles and interactions for the style guide
        ├── fonts           
        ├── img              // Images used ONLY in stylesheets
        ├── variables        // Variables used in the custom theme
        └── styles.less      // Imports of all the base stylesheets
    
    └── components
        ├── alerts       
            └── alert.less   // Custom styles for the alert component. Every component has its own stylesheet that allows to customize it specifically preventing bloat and style leaking.

As a rule of thumb, document those places where clarification is needed. Not every directory and file will need documentation (like in the above example “fonts” is self-explanatory). Put yourself in the shoes of somebody new to the project (or remember those times where you were that person) and provide the guidance you wish you would have been given. The idea is to do this in a way that’s not time-consuming for you but is helpful enough to avoid repetitive questions.

Another key element to point out here is where new styles should be added and any steps that should be followed. The example above demonstrates this, but given the inheritance nature of CSS, it can be worthwhile to state it in detail.

For example, in projects where we used Bootstrap as an underlying framework, we typically have three places where new rules should go, depending on what the developer is trying to achieve. So we added a guide to the documentation comprising three scenarios:

Scenario #1

If you want to overwrite a style defined by Bootstrap, then:

  1. Find out in which stylesheet of the bootstrap framework the rule is defined.
  2. Go to “src/styles/bootstrap-custom”.
  3. Look for the same stylesheet.
  4. If it doesn’t exist, create a new one in that directory, with the same name.
  5. Add your overwrite and point out anything of importance.
  6. Lastly, import the new stylesheet in “src/styles/style.less”.

Scenario #2

If you want to add a new style definition that is not overwriting Bootstrap and that should be available anywhere in the application, then:

  1. Go to “src/styles/custom”.
  2. Find a stylesheet where the new style could be added (think: is this a style for defining a button, or is it a reusable style like a mixin?) and place it where it makes the most sense.
  3. If there isn’t a stylesheet where it makes sense to put this new style, then create a new one.
  4. Name it following our naming conventions.
  5. Add your new style and point out anything of importance.
  6. Lastly, import the new stylesheet in “src/styles/style.less”.

Scenario #3

If you want to add a new style definition for a component (this means it will only be available to that component, wherever the component is used in the application), then:

  1. Go to “src/components”.
  2. Find the component you want to style.
  3. Find the stylesheet for the component, inside of the component directory.
  4. Lastly, add the new style and point out anything of importance.

This guide:

  • Served to keep our styles organized.
  • Kept the styles working according to the inheritance we had established because overwrites were done in the right places.
  • Avoided developers writing overcomplicated rules.
  • Prevented styles leaking to non-intended elements.

3. Establish Your Coding Standards

Your coding standards or CSS style guide refers to the way your team has agreed on writing CSS. This includes the best practices on writing code, like formatting, naming, and syntax conventions. Many companies have shared the way they do it (this article from CSS-Tricks has a great compilation: CSS Style Guides). Here are a couple of ways I find it useful to share this type of information:

Don’ts vs. Do’s List

Use this format to point out the things that you want to avoid, while providing a viable alternative. This removes ambiguity and encourages people to do a specific thing. For example:

Don’ts Do’s
Don’t use tabs for indentation. Do use four (4) spaces for indentation.
Don’t use under_scores or “camelCase” to name classes or IDs. Do use dashes to separate words.
Don’t use Class and ID names to reflect the underlying markup structure. .container-span and .small-header-div are bad names.

Do think about CSS in terms of objects and use simple nouns as names. .global-alert and .badge are good names.

Don’t use IDs and overly-specific selectors to style. Only use these when absolutely necessary (e.g. form controls or page anchors). Do use classes to facilitate reusability and reduce CSS selector specificity conflicts.

Best Practices List

Summarize your guidelines into best practices and include examples. This will make each one easier to read and understand. For example:

Best Practices Example
Write multiple selectors on separate lines. .btn,
.btn-link {
}
Include one space between the selector and the opening brace. .selector {
}
Use shorthand for hex values when possible. #fff vs #ffffff
Write hex values in lowercase. #3d3d3d vs #3D3D3D
Enclose URLs in single quotes. Generally, single quotes are preferred over double quotes, since they’re easier to type. url ('/image.png') vs url ("/image.png")
Don’t use units for zero (0) values, except for angles (deg) and time (s or ms). margin-right: 0; vs margin-right: 0px;

The way one developer writes code can greatly differ from another. This is why it’s important for your team to set coding standards. This ensures that code is consistent across a project, which makes it easier to read, write and review. But make sure that anything that you include in your coding standards is a practice that your team has agreed on.

I worked on a project where we included this in our living style guide. As part of the code, we committed and pushed these best practices to the repo. Then to make sure everybody was on board, everybody on the team had to approve the pull request before we could merge it. This guaranteed that everybody had to make time to review and discuss it.

4. Avoid Long Stylesheets

When you break your styles into smaller and more focused stylesheets it’s easier to document them. You can also save time by not having to document what becomes self-explanatory.

For example, instead of having one 800 line stylesheet with all the variables that you can use in a theme, you can have a file for each of the variable types. This will save time by not having to scroll up and down in the file trying to find something! Think as well of the time that you can save by not having to update the index every time you add or rename a new section.

In a long file, a long index…

/*-------------------------------------------*\
   variables.less

   Index
   - Color Palette
   - Typography
   - Buttons
   - Forms
   - Layout
   - Messaging & Status
   - General
   - Navigation
   - Carousel
   - Jumbotron
\*-------------------------------------------*/

Breaking a file, no index is necessary:

Another example to consider when working in large applications is the modlet workflow. It explains why working with smaller files organized by components allows testing and assembling them in your app more easily.

5. Document CSS With a Style Guide in Mind

A big part of documenting CSS properly has to do with writing good CSS and vice versa. This means that even when the state of your CSS code base might not be the best, enforcing documentation rules can move you towards a better system.

This is where documenting CSS with a style guide in mind comes into place. The idea behind it is that a style guide can help you determine a good structure for your CSS because to create one you will need to distinguish between:

  • the baseline styles that define the look and feel of your application (including any CSS frameworks that you are using)
  • the customizations that are done to specific components, and
  • the customizations that are done to specific pages.

The bulk of your CSS should be comprised of the baseline styles, as they are available anywhere in the application and affect all elements in their default state. Custom styles should take place as you add components with a specific look and behavior, or in the cases where the layout of an element or component in a page requires it.

A great way to capture how this specific setup can work in your application is to create a  style guide sitemap. Once you know how a style guide looks like in your application, you can document elements with that in mind. For example, if you have defined in your style guide how buttons and navigations look, it’s clear cut where you should add new styles and documentation for them (in “buttons.css” and “navs.css”).  But what about a navigation that’s made of buttons?

Having a style guide can help you make this decision, as you can compare how buttons and navigations look, from a display and a markup perspective. Let’s look at this example:

<button type="button" class="btn btn-primary">Primary</button>
<button type="button" class="btn btn-secondary">Secondary</button>
<button type="button" class="btn btn-emphasis">Emphasis</button>

<button type="button" class="btn btn-primary disabled">Primary</button>
<button type="button" class="btn btn-secondary disabled">Secondary</button>
<button type="button" class="btn btn-emphasis disabled">Emphasis</button>
<ul class="nav nav-tabs">
     <li class="active"><a href="#">Active</a></li>
     <li><a href="#">Nav Item</a></li>
     <li><a href="#">Nav Item</a></li>
     <li class="disabled"><a href="#">Nav Item</a></li>
</ul>

In this case, there are two possible locations for the CSS that will define the navigation made of buttons:

  1. If the markup follows the structure of other navigations, using a list of links, or a <nav> with links that look like buttons, then add the nav styles to “navs.css”.
  2. If the markup that you will use is <button> then, add the styles to “buttons.css”. You could even add it as a separate stylesheet (like “buttons-group.css”). In this case, the term “navigation” wouldn’t be appropriate any longer since HTML buttons are less accessible as navigational items.

6. Breakdown Your Stylesheets Into Sections

Once you have broken down your stylesheets into more manageable files, then you can continue this exercise by breaking down each style into individual sections.

To begin with, each stylesheet should at least include a title and (when useful) a short description. The title could be as simple as the name of the file, capitalized to look more like a title (ex: “Buttons” for the stylesheet “buttons.css”), or it could be the same as the name of the file, like this:

/**
 * icons.css
 */

.icon {
  font-family: 'bitovi';
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
}

I find using the filename particularly useful when debugging the code in the browser, and mostly when the file has been compiled with other files, as I can get a reference to the file where the style lives.

Also, note that the comment style that I used opens with  /** vs just /*. This is a convention used in JSDoc to parse comments that should be included in the auto-generated documentation. I recommend using this style, as many living style guide generators use the JSDoc format, so when you are ready to use a generator, your code will need very little additional work.

In any case, you can use other styles to denote a section such as:

/*-------------------------------------------*\
    icons.css
\*-------------------------------------------*/

.icon {
  font-family: 'bitovi';
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
}

To some extent, this depends on what your team agrees is the best way to make a section stand out. The only requirement is to use /* at the beginning and */ at the end. What really matters is that whichever approach you use, you stick to it and use it across your CSS code in a consistent way.

If you think a description might be useful in a particular stylesheet, then add it as part of this first comment. For example:

/**
 * icons.css
 *
 * Icons should convey in a simple and meaningful way the concept of the function
 * they represent. When designing new icons be sure to remove any complexities 
 * and follow the linear and lightweight appearance of the icon set.
 */

.icon {
  font-family: 'bitovi';
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
}

Doing this will reinforce the idea of it being a section. Also, try to break down the description into multiple lines (Harry Roberts recommends up to 80 characters) so that it’s easier to read while having multiple files open or while reading it on Github.

After adding a title and a description, you can go a step further by breaking down the styles within the stylesheet into sections. To do this think about how logically explaining the contents of a stylesheet makes sense. For example, the stylesheet “buttons.css” will typically have a section where the default style of a button is defined by just applying the class .btn. Then there will be additional styles that define different colors, sizes, and configurations that can be applied in conjunction to further customize its appearance and behavior. Creating sections for each of those styles will make it easier to understand and find where new classes or overwrites should appear. Also, it is less intimidating to look at a file when the code is presented in snippets versus a long file where it is hard to tell where styles begin and end.

Let’s look at this comparative example. First, a LESS code block without sections:

.label-condensed-variant(@color) {
  &:before {
    background-color: @color;
  }
}

.label {
  border-radius: 0;
  font-family: @font-family-bold;
  font-size: 85%;
  position: relative;
  &.label--condensed {
    font-size: @font-size-xsmall;
    color: @gray;
    background: transparent;
    padding-right: @padding-small;
    &.label--primary {
      .label-condensed-variant(@label-primary-bg);
    }
    &.label--success {
      .label-condensed-variant(@label-success-bg);
    }
    &.label--info {
      .label-condensed-variant(@label-info-bg);
    }
    &.label-warning {
      .label--condensed-variant(@label-warning-bg);
    }
    &.label--danger {
      .label-condensed-variant(@label-danger-bg);
    }    
  }
  &.label--simple {
    font-family: @font-family-base;
    font-size: @font-size-xsmall - 1;
    color: @gray;
    border: 1px solid @gray-light;
    padding: 2px;
    border-radius: @border-radius-small;
    text-transform: uppercase;
  }
}

And the same code block with sections:

/**
 * bootstrap-custom/_labels.less Labels
 * 
 * Overwrites the default styles defined by the bootstrap framework.
 *
 */

.label {
  border-radius: 0;
  font-family: @font-family-bold;
  font-size: 85%;
  position: relative;
}


/**
 * Condensed Labels
 *
 * Modifies labels to provide a smaller and narrower version with a colored circle to be used in areas with little space (for example in list views).
 */

.label {
  &.label--condensed {
    font-size: @font-size-xsmall;
    color: @gray;
    background: transparent;
    padding-right: @padding-small;    
  }
}


/**
 * Condensed Labels - Colors
 */

.label-condensed-variant(@color) { // Variant mixin to set the circle color
  &:before {
    background-color: @color;
  }
}

.label {
  &.label--condensed {
    &.label--primary {
      .label-condensed-variant(@label-primary-bg);
    }
    &.label--success {
      .label-condensed-variant(@label-success-bg);
    }
    &.label--info {
      .label-condensed-variant(@label-info-bg);
    }
    &.label--warning {
      .label-condensed-variant(@label-warning-bg);
    }
    &.label--danger {
      .label-condensed-variant(@label-danger-bg);
    }
  }
}


/**
 * Simple Labels
 *
 * Modifies labels to provide a simple linear version where colors are not used.
 */

.label {
  &.label--simple {
    font-family: @font-family-base;
    font-size: @font-size-xsmall - 1;
    color: @gray;
    border: 1px solid @gray-light;
    padding: @padding-small;
    border-radius: @border-radius-small;
    text-transform: uppercase;
  }
}

7. Index the Contents of Your Stylesheets

This is a great way to provide a snapshot of what’s in the stylesheet and a must in those projects where, for whatever reason, long stylesheets are there to stay (not a fan of those projects, but they do happen!).

A stylesheet index typically looks like this:

/**
 * icons.css
 *
 * Icons should convey in a simple and meaningful way the concept of the function
 * they represent. When designing new icons be sure to remove any complexities 
 * and follow the linear and lightweight appearance of the icon set.
 *
 * Index
 * - Icon Font
 * - Icon Variations
 * - Icon Animations
 *
 */

And although I love how neat and useful they can be, I have to admit they can be easily forgotten, and therefore outdated. They are also a pain to update when they are long and you are using numerals (so avoid those!)

An alternative way of using indexes is to let a style guide generator do the work for you by looking at your stylesheets, finding the sections that you have defined and generate an index for you. I will expand more on this topic at the end of this article.

8. Find the Sweet Spot of Documenting

Here is where the secret of documenting lies. It’s easy to get carried away and go into a documentation frenzy once, to then forget about it and end up with only a portion of your codebase over-documented and the rest undocumented. As with everything in life, the secret is finding the balance. Document those areas where attention is needed because there are unforeseen dependencies, additional resources, or important notes to have in mind. That is to say that not every bit of code should be documented but it is definitely useful to break it down into chunks and explain what those chunks are when necessary. In this way, documentation becomes a useful tool that is part of your workflow and not an afterthought that you avoid to do. But how do you exactly do that? Here’s an example:

Let’s say that you are going to implement the markup and CSS for the following card component:

card base design

Looking at the designs you can identify the following style patterns:

  • The card base design
  • The cards grid
  • The cards list
  • The card dark version

You can then break down the CSS implementation with those patterns in mind and use the documentation to guide you. To start with, your “cards.css” stylesheet can include a simple intro as follows:

/**
 * cards.css
 *
 * These are the styles for the card component.
 *
 * Index
 * - Card Base 
 * - Card Grid
 * - Card List
 * - Card Dark
 */

You can include more useful information in the introduction, but since you are just getting started something simple can help lay the documentation skeleton.

Then, let’s add the sections where you will be working your styles in:

/**
 * cards.css
 *
 * These are the styles for the card component.
 *
 * Index
 * - Card Base 
 * - Card Grid
 * - Card List
 * - Card Dark
 */


/**
 * Card Base
 */


/**
 * Card Grid
 */


/**
 * Card List
 */


/**
 * Card Dark
 */

With these sections in mind, you can visualize how the code should be structured. You know that you should make the base definitions of the card flexible and independent enough that you can easily make the card work in a grid, list or dark versions.

Then as you write the code, you can get more specific with your comments:

/**
 * Card Base
 *
 * Defines the appearance and behaviour of the default card with its main parts:
 * - Card Image
 * - Card Content
 * - Card Footer
*/

.card {...}
.card__image {...}
.card__logo {...}
.card__content {...}
.card__footer {...}

I consider this the basic level of documentation you should include because it serves as a guide to layout the code and quickly informs how things are organized for the next person who works on it. 

The next level is adding comments that are specific to a rule, and that can be used to explain what this rule is doing because it is not obvious at a glance. For example:

/**
 * Card Base
 *
 * Defines the appearance and behaviour of the default card with its multiple parts:
 * - Card Image
 * - Card Logo
 * - Card Content
 * - Card Footer
*/

.card {
  @media (max-width: {{ screen-xs }} ) {
    border: none;   /* because the card takes 100% of the screen on mobile */
  }
}
.card__image {...}
.card__logo {...}
.card__content {
  flex: 1 1 auto;   /* "auto" needs to be explicit to avoid the div collapsing on IE.*/
}

The beauty of this approach is that the documentation is there to support and inform the implementation as you go, versus something that is added at a later time.

Here are a couple more examples of the Bootstrap framework that show when comments are useful and when it is worth going into more detail.

Example #1

// Need .dropdown-toggle since :last-child doesn't apply, 
// given that a .dropdown-menu is used immediately after it

.btn-group > .btn:last-child:not(:first-child),
.btn-group > .dropdown-toggle:not(:first-child) {
 .border-left-radius(0);
}

This comment clarifies why these styles exist and what they are doing. It’s also short and to the point, communicating the idea in a casual language.

Example #2

// Checkbox and radio options
//
// In order to support the browser's form validation feedback, powered by the
// `required` attribute, we have to "hide" the inputs via `clip`. We cannot use
// `display: none;` or `visibility: hidden;` as that also hides the popover.
// Simply visually hiding the inputs via `opacity` would leave them clickable in
// certain cases which is prevented by using `clip` and `pointer-events`.
// This way, we ensure a DOM element is visible to position the popover from.
//
// See https://github.com/twbs/bootstrap/pull/12794 and
// https://github.com/twbs/bootstrap/pull/14559 for more information.

[data-toggle="buttons"] {
 > .btn,
 > .btn-group > .btn {
   input[type="radio"],
   input[type="checkbox"] {
     position: absolute;
     clip: rect(0,0,0,0);
     pointer-events: none;
   }
 }
}

This is a great example of documentation that goes more in depth, explaining the logic behind an implementation decision and providing links to additional information.

Taking it a Step Further

With these best practices in mind, the next step is to incorporate a living style guide as part of your documentation. A living style guide is a living document that shows the comments that you have included in your code structured like a website, so you can navigate the documentation separately from the source code.

What makes living style guides powerful is that the actual documentation lives with the code and can be easily updated as your code changes, allowing it to stay in sync and relevant. The additional advantage is that you can make this documentation available to other people on your team who might not be interacting directly with the code (like designers, product managers, or QA engineers). These team members would also find it helpful to know how the UI is shaping up.

In a living style guide, you can include interactive demonstrations of your code and you can further organize your documentation independently from the code structure.

Conclusion

Documenting CSS begins with clear rules and a well-structured code base. When done as part of your workflow, it can also serve as a guide to structure your code and keep it organized as it grows. This has the additional benefit of making it clear where things are and where new code should be added, easing the onboarding of new members while fast-tracking development.

Useful Resources

Reminder: 8 Best Practices

  1. Set the ground rules
  2. Find the sweet spot of documenting
  3. Index the contents of your stylesheets
  4. Breakdown your stylesheets into sections
  5. Document CSS with a style guide in mind
  6. Avoid long stylesheets
  7. Establish your coding standards
  8. Explain the structure of your code base

Leave a Reply

Your email address will not be published. Required fields are marked *