Pothibo

HTML5 to create awesome CSS

I have recently come to the conclusion that no matter how I write my CSS, it always ends up being a pile of unmaintainable code. It's not that I'm bad at writing stylesheets, but I start writing clean code and then, requirement changes, I make small edit. Or I make a few fixes here and there and before I know it, spaghetti all over the place!

So I decided to take a step back and look at what I was doing and came up with a solution that I've really started to like. As I start showing off how I do CSS, keep in mind that I'm always taking readability over performance. That's because I think that building readable CSS is much easier to optimize down the road then the other way around.

With readability in gear, I chose to compose my CSS with 3 components:

  1. Root selector;
  2. Leaf selector (optional);
  3. Status selector (optional);

The root selector

The root selector is a selector that is used to determine a component on the webpage. It usually is an id but can be a class too. The only thing that matters is that it should use the same scheme throughout the app.

example.html
<div id="Posts"> <ul> <li>A post</li> </ul> </div>
example.css
#Posts { margin: 8px 24px; }

This way, it's easy to browse the CSS file by locating the nearest root element.

Leaf selectors

Leaf selectors can vary in width but as a rule of thumb, I keep them as small as possible. Their goal is to determine what element you want to style.

To understand what I mean, consider this HTML:

<section id='Posts'>
  <header>
    <h2>Your posts</h2>
    <a class='new' href='/posts/new'>New Post</a>
  </header>
  <ul>
    <li><a href='#'>A Post</a></li>
  </ul>
</section>

There are two anchor elements that needs their own styling. Leaf selector are built to keep the DOM clean and readable.

#Posts header a.new {
  display: block;
  border-radius: 3px;
  padding: 4px 8px;
  background-color: rgb(100,100,180);
}

#Posts ul a {
  text-transform: uppercase;
}

Status selectors

Status selectors are where the HTML5 kicks in. Previously, I would have toggle classes to indicate statuses. With data-attributes however, the possibility are much more interesting.

CSS classes are boolean by their nature, they exist on the element or they don't. And managing boolean flags can be cumbersome for both the CSS environment as well as the JavaScript one. Instead, we can use the data-attribute and have CSS match against the value of that attribute.

To illustrate the use of data-attribute, here's the same tag that we used previously.

<section id='Posts'>
  <ul>
    <li data-status='published'>
      <a href="#">A post title</a>
    </li>
  </ul>
</section>

The status can be one of 3 things:

  • draft;
  • published;
  • deleted;

Taking everything discussed so far, the styling of all possibilities become very easy.

#Posts li[data-status='published'] {
  color: black;
}

#Posts li[data-status='draft'] {
  color: grey;
}

#Posts li[data-status='deleted'] {
  color: red;
}

Because the three states are mutually exclusive, I don't have to worry about unexpected situation where a post would have more than 1 status to it.

And using data-attributes in JS is also very straightforward.

var li = document.querySelector('li');
li.dataset.status = 'published'

Sass nesting limit

Sass allows for nesting and one thing I notice is that if I go deeper than 1 level of nesting, things become messy very fast. For that reason, I only use nesting when I want to add a status to a selector or if the group is less than 50 lines of code and has no nesting of its own.

#Posts li {
  padding: 4px 8px;
  
  &;[data-deleted] {
    color: $grey;
 }
}

Avoid global classes

The goal with this approach is to avoid global CSS classes as much as possible. Instead, I want to use variables(Sass) when possible.

There might be scenario where global classes are useful. One situation would be where I would want to have the same button style applied to every element that are buttons on the page.

input[type=submit], button, .button {
  border-radius: 4px;
  font-family: inherit;
  font-size: 1em;
  padding: 8px 12px;
  text-align: center;
}

I'm not using colors in those global classes because they often change depending on the state of the object. What I will do, however, is store colors in variables so I can easily change them when necessary.

variables.scss
$blue: rgb(5, 96, 6);

A note on performance

One thing I don't look for when writing CSS is performance optimization, or performance at all. Considering everything that goes into building a page (HTML, JS, CSS, images, etc.), I have yet to come to a site where the problem is the way I write my CSS. Also, browsers do not have strong development tool to help diagnose problems with CSS. This being said, if I have to choose between a very easy to read CSS codebase and an optimized one, I'll always be choosing the first one.

Get more ideas like this to your inbox

You will never receive spam, ever.