More Meaningful CSS

Tim Baxter wrote an article for A List Apart discussing the possibly dogmatic, and maybe unnecessary, use of classes for styling HTML elements.

Why do ‘.button’ when you can do ‘button’? Why do ‘.form’ when you can do ‘form’?

I think it’s important to understand the context of which class-based systems like OOCSS, SMACSS, and BEM have made their way into being.

With large, oft-changing web applications, there are certain problems that arise that don’t occur on simpler, less dynamic web sites.

My blog, for example, is a relatively straightforward site. I’ve made only minor revisions to the design over the last 7 years and my entire CSS weighs in an easily manageable 6 KB uncompressed.

There’s not a lot of complexity to the site design. I could use element selectors liberally. I could and have returned to that code after years of neglect and understand the code relatively quickly.

So then, why the need for classes on everything as advocated by myself and others?

These systems address the complexity of not only large projects and large teams but also of changing designs and changing code.

Back when I was at Shopify, we had inputs. Every project has inputs. They were styled using input selectors.

One of the problems we ran into was a redesigned input style that needed us to update the HTML structure around our input elements. Where this was especially problematic was trying to update the style and HTML across the entire application in stages.

Using input selectors meant that we needed to write extra CSS to undo the input styles to then be able to apply the CSS that we wanted via a class. We did this using a mixin to make it clear that this code was unwanted and that we could remove this mixin once the transition to the new style was complete.

When you have multiple teams working away on multiple branches, you can’t stop the presses so that you can apply site wide changes. Using element selectors inhibits the ability to make incremental changes to a project because you can’t be judicious with their use. Element selectors apply to all occurrences.

Another problem is that HTML5, while it introduces a number of new elements, does not go far enough to describe all of the unique components of a design. Nor do I think HTML ever will or ever should. (See Web Components.)

An element may also look different under different contexts. Not all navs look the same. Not all buttons look the same. Which requires you to concoct a selector for a specific context.

You can always come up with element selectors for a specific scenario, a specific combination of HTML. But large projects change and with those changes, requires the ability to decipher what the CSS is doing and under what conditions, in order to be able to change it at will. Otherwise, you end up with code you don’t understand and a reluctance to delete it or change it for fear something might break.

This is where class-based CSS systems like SMACSS excel. You can give something meaning with a name—a name that you and your team can decide together what it means.

With that class name comes a simplicity to your CSS. With a succinct grouping of classes, we begin to develop boundaries between objects on the page, like borders around a country. Each object has its own design and rules.

It’s no longer a single page, static in time. It’s a collection of pages, built with an array of components that change from page to page and change in design and composition over time.

A rigid system will break under the heavy winds of an ever-changing project. A flexible system will bend to your will, allowing your project to grow with greater ease.

Meaningful CSS is important. It’s also important to ask where and how we give it that meaning.

Published May 17, 2016
Categorized as HTML and CSS
Short URL: https://snook.ca/s/1083

Conversation

9 Comments · RSS feed
Nathan Smith said on May 17, 2016

YES, x1000 this. Reading that ALA article, I had two thoughts…

1. ALA used to have good content.

2. What year is it? Did we go *back*?

:)

Josh Northcott said on May 17, 2016

Exactly. I can't help but think about Micah Godbolt's book about "Front End Architecture" and being a Frontend Architect. As devs, we need to architect a design system in a way to be component driven to provide that flexibility over time.

Is it just me or does it seem like many of us out there have been doing this for a while but now maybe it's taking on more of a larger adoption? Kinda of along the lines of what my good friend Nathan Smith just said above. ;)

Benjamin Listwon said on May 17, 2016

As always, a well-considered response.

It got me to thinking though, at least as a mental exercise, if in fact a class-based system is absolutely "required" for a large-scale project/product.

Your examples from Shopify (et al) show a range of scenarios that make use of classified CSS quite fittingly.

But a part of me also wonders: if an organization truly has honest-to-goodness semantic markup and properly isolated, modular styles, then wouldn't a custom scenario (or migration) be relatively simple by utilizing the class attribute as it was (sort of) originally intended?

That is to say, should not a "class" of something simply be a subtype of the usual thing it is. Ergo, a signup form is still a type of form.

I'm sort of thinking out loud here, so please forgive me where I'm covering already covered ground. Just want to keep the conversation going, if I can.

---

Let's consider some styles for a hypothetical site navbar for a second.

HTML


<nav>
  <ul>
    <li><a href="/">Dashboard</a></li>
    <li><a href="/issues">Issues</a></li>
    <li><a href="/reports">Reports</a></li>
  </ul>
</nav>

CSS


nav {
  background: #369;
}

nav a {
  color: #fff;
}

Now, if a new feature came along, where we wanted a specialized scheme for the pages in that section, we could do something like:


body.special-feature nav {
  background: #333;
}

-- or --

nav.special-feature {
  background: #333;
}

If we used a preprocessor (for me that's LESS, hence the LESS examples), then our job gets a little easier because we could have variables for this kind of thing. That way, we can reuse those color references throughout the CSS (which would likely be far larger than this example :)


@brand-primary-color: #369;
@brand-highlight-color: #fff;

@special-feature-primary-color: #333;

nav {
  background: @brand-primary-color;
}
a {
  color: @brand-highlight-color;
}

body.special-feature {
  nav {
    background: @special-feature-primary-color;
  }
}

Now, quite often, this is what gets out of hand. As you rightly point out, folks concoct selectors for each new feature, or whenever something needs to adapt to a new scenario. Tracking that down in a tangled web of CSS can get tough, and comprehending the cascade that leads to its effect on the output can be nightmarish.

Knowing what I do about SMACSS, OOCSS and other systems though, I still think the best solution lies not in the intricacies of naming the classes, but in separating the major scenarios that have an effect on the rendering. In the above scenario, that means something like:

Files


less
|- styles.less
|- variables
|   |- main.less
|   |- special-feature.less
|- modules
|   |- navbar.less

styles.less


@import "variables/main.less";
@import "variables/special-feature.less";
@import "modules/navbar.less";

navbar.less


nav {
  background: @navbar-background-color;
}

a {
  color: @navbar-highlight-color;
}

Where, of course, you can still set your newly minted @navbar-background-color to @brand-primary-color by default and provide its override in special-feature.less.

It could be argued this just kicks the can further up the chain, but it does utilize the principles of containment SMACSS (or many other modular CSS systems) advocate, without littering the class namespace in the CSS or the HTML.

---

Also, as you correctly point out, several HTML5 elements still lack a lot of clarity in practice. section springs immediately to mind.

This may not have been where Tim was going with it, but I think the use of ARIA roles is a decent suggestion for adding clarity in those situations, both in the markup and styles.


<nav>
  <ul role="menubar">
    <li role="menuitem"><a href="/">Dashboard</a></li>
    <li role="menuitem"><a href="/issues">Issues</a></li>
    <li role="menuitem"><a href="/reports">Reports</a></li>
  </ul>
</nav>

Now, if you wanted all menuitem elements to be purple, regardless of their location, you have a candidate selector to do so.

Again, it can be argued that this is nothing more than shifting the role of a classname to a role attribute. However ARIA provides a standardized definition we can all follow, rather than relying on classnames that are meaningful only to an individual or team.

Consider a new hire coming across the above markup. If it is known that we follow ARIA guidelines, those items can be immediately conceptualized, versus the potential time wasted on poorly chosen or less descriptive classnames.

---

While I do not, in any way, doubt or question the examples you wrote about (how can one judge what one has not experienced), it is hard for me, having worked on small and (very) large scale systems alike, to imagine a scenario that cannot be tackled with semantic HTML, modular CSS strategies and a carefully considered approach to reusable structures.

Even after utilizing Angular, Polymer and Vue for a few different projects now, I find that templated modules built primarily from core HTML5 elements are easier to manage, style and reuse.

I think that most organizations' troubles do not stem from semantic HTML and semantic CSS being too restrictive or not expressive enough, but rather from years of legacy spaghetti CSS piling up due to a myopic focus on feature creation and not on addressing the underlying system.

But once again, every case is different and I don't want to be prescriptive. Thanks again for a great post, and pointing me to a couple great articles today.

Thierry Koblentz said on May 17, 2016

I agree with Nathan, ALA is not what it used to be but unfortunately people still look at those articles as best practices...

Benxamin said on May 18, 2016

I agree with a "tags only" minimal, semantic approach to start. Frameworks do this in a reset and/or normalize anyway. But I also see that we still need classes to represent the various states of different components. These can be added as needed.
It is like the "functional first" programming style where you don't add/implement side effects until the very end.

Ajay said on May 20, 2016

The ALA article seemed a bit like a personal F.U. at Bootstrap, without which we probably wouldn't have ever ended up with <form class="form"> - but I do think their article does hold some merit.

That's not to say that Bootstrap is complete BS. It's a solid framework if that's what you need and their excessive use of classes is also needed to make the framework so robust (if used properly as ALA said).

But if you don't need that, there's nothing wrong with some meaningful classes; and they should be meaningful.

Classes make it easier to apply styling because you can visualise where the code is being used. e.g. It's much harder to decipher

main article p:first-of-type a{} 

than it is to decipher

article a.link-as-button{} 

As everything on the interweb; there is no right answer.

Chuck Neely said on May 20, 2016

@BenjaminListwon you may already know this, but you could slightly simplify your LESS example by using '&' as a qualifying selector. So this:


nav {
  background: @brand-primary-color;
}
a {
  color: @brand-highlight-color;
}

body.special-feature {
  nav {
    background: @special-feature-primary-color;
  }
}

Can become:


nav {
  background: @brand-primary-color;
  body.special-feature & {
    background: @special-feature-primary-color;
  }
}
a {
  color: @brand-highlight-color;
}

...and the output is the same. Sure it only saves a coupe of lines of code, but I've fallen in love with this method recently as it allows you to keep related code blocks in the same nest, keeping things contextual. Anyway, just thought it was worth mentioning.

Ronny Karam said on May 24, 2016

Well written.

Unfortunately every developer, designer and coder have a different opinion about what is and isn't a best practice.
I prefer to go with whatever gives me flexibility and maintainability; again, those 2 words differ from person to person.

Sacha Greif said on May 25, 2016

Honestly at this point we've gone from styling using inline HTML attributes, to using external CSS, to now using inline CSS styles with React; and from completely banning presentational classes to saying that actually no, presentational classes are fine too.

I'm pretty sure that for every "best practice" CSS article you can find another, more recent one that says the opposite.

At this point when it comes to CSS I've only kept two rules throughout the years:

1) Be consistent.
2) Don't be dogmatic.

In other words if you come up with a system stick to it, but don't be afraid to change it if a better system comes along.

/rant

Sorry, comments are closed for this post. If you have any further questions or comments, feel free to send them to me directly.

Want to learn about scaling CSS for large projects?

I'm available for full and half-day workshops on scalable CSS architecture. I can provide on-site training for your team. Interested?
Get in touch.