Skip to content

Response to "Meaningful CSS: Style Like You Mean It"

This post is in response to Tim Baxter's A List Apart article "Meaningful CSS: Style Like You Mean It".

It's an excellent article well worth a read and offers some pointed criticism against the current CSS practices that developers, such as myself, use when building and styling websites. If you haven't read it yet, none of this post will make sense, so go read it now and I'll be here when you're done.

We nest div inside div inside div, and we give every div a stack of classes—but when we look in the CSS, our classes provide little insight into what we’re actually trying to define.

I propose that this isn't necessarily a bad thing. CSS isn't supposed to be descriptive of the content, that's HTML's job.

Even when we do have semantic and meaningful markup, we end up redefining it with CSS classes that are inherently arbitrary. They have no intrinsic meaning.

CSS classes are arbitrary by definition. That is their job. They're meant to be used as styling hooks without implying anything about how the content should be read or parsed.

Why can’t our CSS be as semantic and meaningful as our markup?

The HTML spec says the following about "Semantics":

Elements, attributes, and attribute values in HTML are defined (by this specification) to have certain meanings (semantics). For example, the ol element represents an ordered list, and the lang attribute represents the language of the content.

Likewise, the spec says the following about the class attribute:

The attribute, if specified, must have a value that is [space separated] representing the various classes that the element belongs to…authors are encouraged to use values that describe the nature of the content

Comparing this to the description of any HTML element, it's clear that the class attribute does not impart any semantics whatsoever on affected elements.

More on this later.

We can create semantic, descriptive, and meaningful CSS that understands what it is describing and is as rich and accessible as the best modern markup.

When "semantics"—a word that literally means "meaning", which makes discussions difficult due to ambiguity and devolving into meta arguments about what the word "means" means—is used in the context of web development, it is generally used to refer to "browser semantics" which is specifically about instructing browsers how to automatically interpret the content, such as for use with assisted navigation. For example, the "semantics" of an <h1> element allow the browser to identify that content for screen readers as being a primary heading.

All of that said, it's easy to conflate "semantics" (HTML) with "semantics" (meaning) especially with regard to CSS, which is layered on top of HTML in ways that can actually affect semantics (such as by using display: none to remove a node from the content flow).

So to continue from the spec quotations above and answer why our CSS can't be as semantic and meaningful as our markup:

CSS does not have semantics. Per the spec only HTML does. class attributes do not add semantics. <button class="button"> is just as much a button as <button class="foo">. This ability is very useful, and I will cover more of why later.

We can define the elephant instead of saying things like .pillar and .waterspout.

This is such an apt note. Especially for this particular article which has raised the hackles of so many front-end developers' dogmatism that I must include the Buddha's verse:

O how they cling and wrangle, some who claim
For preacher and monk the honored name!
For, quarreling, each to his view they cling.
Such folk see only one side of a thing.
– Buddha

We write <table class="table"> and <form class="form"> without a moment’s hesitation. Looking at Github, one can find plenty of examples of <main class="main">. But why?

The name is merely a convenience, it could equally be <table class="dGFibGU"> and it would have identical semantics. What's important here at scale is portability. For small sites you can ensure that you will always want to do things a particular way. As the size and use of a website grows, the number of edge cases will increase drastically.

I will illustrate this with a simple example of <form class="form"> being useful. It is a contrived example, but not uncommon in my experience.

It is very easy for a company to start a site in one language, such as PHP with Wordpress. As the company grows its needs change and suddenly they have need of a one-off page that happens to be served from ASP.NET using WebForms. WebForms requires that the entire page be wrapped in a <form> element, which means the search form that previously had been:

<form class="search-form" ...>
  <input type="search" class="search-form__search" ... />
  <input type="submit" class="search-form__button" ... />

now must be:

<div class="search-form" ...>
  <input type="search" class="search-form__search" ... />
  <input type="submit" class="search-form__button" ... />

Without the classes, the styles are no longer portable.

One could certainly argue about the "bad choices" of using Wordpress and PHP, or ever using WebForms instead of [insert buzzword of the month], but businesses rely on the work getting done. As a developer, it's my job to make sure the work gets done well, and quickly.

You can’t have more than one main element, so you already know how to reference it directly.

This isn't quite accurate, but I already commented on the article about this issue.

Why reinvent the semantic meanings already defined in the spec in our own classes? Why duplicate them, or muddy them?

This is what I'll call the "If it looks like a duck" argument for styling. It's not a bad argument, but it has its failures. What happens when you come upon a wolf in sheep's clothing (or two guys dressed as a tiger as it may be)?

It's easy to say that <button class="button"...> is redundant, but it's not uncommon to have <a class="button"...> or <button class="link"...>.

This brings me to an important point. There is a definite distinction between what something means, and how it looks.

You should care about bloating your markup and slowing down the user experience.

Redundant classes aren't the cause of the majority of the bloat on the web. Priority targets for reducing bloat are:

  • media
  • unnecessary javascript frameworks
  • gzipping data from the server

Gzipping handles repetition well. I've never found the use of classes to be a big enough bottleneck that they were even relevant for optimization. If I did, I'd start by minifying the class names to things along the lines of class="à x ò"

And if you’re getting paid to do this stuff, you should care about being the sort of professional who doesn’t write redundant slop.

I think this single ad hominem line above all others undermines the credibility of the rest of the article. I found it to be jarring and out-of-place in an otherwise civil and well-written article.

The first step to semantic, meaningful CSS…

There's that "semantic CSS" concept again.

…is to start with semantic, meaningful markup. Classes are arbitrary, but HTML is not. In HTML, every element has a very specific, agreed-upon meaning, and so do its attributes. Good markup is inherently expressive, descriptive, semantic, and meaningful.

This paragraph does a 180°. It starts by talking about the "meaning" of CSS, and then explains how it holds no meaning.

Once you have <header role="banner">, adding an extra class is simply redundant and messy. In our CSS, we know exactly what we’re talking about, with no possible ambiguity.

In your CSS you'll know exactly what you're talking about only if all of your banners are the same.

This is a problem of scale and growth.

If you're building a small site, and you've got a single banner on the home page, by all means use [role="banner"]. It would make sense and be appropriate.

If you're making a large site, and you know there are going to be numerous marketing campaigns that will each have their own landing page or pages, each of which will have a [role="banner"] and each of which may have its own design or variation, it would not make sense and would be highly inappropriate.

If you're building a small site that might some day turn into a large site, I would recommend using the large-scale approach to avoid the added maintenance costs involved with either extended updates or a rewrite.

A word of caution: don’t throw ARIA roles on elements that already have the same semantics. So for example, don’t write <button role="button">, because the semantics are already present in the element itself.

I absolutely agree with this, however I honestly haven't seen anyone attempt this particular form of redundancy in the wild.

Good markup does not change from project to project.

I find that the structure generally remains the same, however the classes, IDs, names, and other content attributes must change to be useful.

If you’re writing semantic, accessible markup already, then you dramatically reduce bloat and get cleaner, leaner, and more lightweight markup.

      <a href="">example</a>

has the exact same semantics as

<a href="">example</a>

and both are equally accessible.

if you’re currently wrangling div-and-class soup, then you score a major improvement in accessibility

Divs are semantically transparent and will not change accessibility in any way. The CSS and JavaScript applied to those divs may change accessibility, but that is equally true of semantic markup.

You’re strongly encouraging a consistent visual language of reusable elements.

Classes strongly encourage a consistent visual language of reusable elements. They're even more reusable than element and [aria-*] attribute selectors because the nodes that they may be applied to are a superset of the aformentioned element and [aria-*] attribute selectors.

This becomes more important as things change. Tying styles to classes rather than specific elements allows the markup to be updated to use the best practices and most appropriate semantics independently of the CSS.

What used to be <input type="text"> can now be <input type="search">, but if you were using input[type="text"] as a selector, you couldn't make the change in HTML alone.

You’ll have more consistent markup patterns, because the right way is clear and simple, and the wrong way is harder.

This is an admirable goal, but it's a false dichotomy. With many projects it's appropriate to have more than one way to style the same basic thing. Sometimes buttons are solid. Sometimes buttons are hollow. Sometimes buttons look like links. Sometimes buttons look like tabs.

You don’t have to think of names nearly as much. Let the specs be your guide.

Sometimes the feature you're building doesn't have an appropriate equivalent in the spec. Naming things is hard.

It allows you to decouple from the CSS framework du jour.

I highly recommend a particular flavor of BEM and avoiding most CSS frameworks. CSS frameworks often use selectors with high specificity, and I believe that developers who can't replicate the styles that are in those frameworks probably shouldn't be writing CSS. For larger projects you're going to override practically all the styles anyway that the gains are moot.

Here’s another, more interesting scenario. Typical form markup might look something like this…

"The difference between theory and practice is greater in practice than it is in theory."

This example on its own seems valid, but it makes no attempt to solve the common problem of having a page with a contact form and a search form.

While it would be easy to say that they should use similar markup and similar styles, the reality is that the vast majority of sites have wildly different markup and styles for the two forms because they serve different purposes and are at differing priority on the page.

If you were to style the basic form elements so that they suited a contact form, you would then need to override them to style the search form. If you later make changes to the styles of the contact form, you are at risk of breaking the search form.

If instead you were to leave the styles attached only via styling hooks (typically classes) there is no risk of a change to one form breaking the other.

CSS class-naming patterns place rigid demands on the markup that must be followed.

I would say that form > p (from the suggested selectors previously mentioned) makes a greater demand on the structure of the markup than .form__group. Certainly .form__group must be within a .form, but it doesn't specify where in the form, nor does it specify how deep. This allows for intermediate containers to be used, or not used, on an as-needed basis.

I would call such an allowance flexible compared to the constraints of form > p.

One possible argument might be that ensuring all team members understand the correct markup patterns will be too hard.

I find that ensuring all team members understand any concept to be challenging as a general rule.

if there is any baseline level of knowledge we should expect of all web developers, surely that should be a solid working knowledge of HTML itself, not memorizing arcane class-naming rules.

I wish that it were possible to expect all web developers to have solid working knowledge of HTML. They often don't. Web developers are not taught out of the spec; they often learn secondhand from friends or Stack Overflow or worse.

The best way I've found to get everyone on the same page is to prioritize training and code review. When developers are properly trained to use a system, whether it's styling based on HTML semantics or Atomic OOBEMITSCSS, they will do a better job and produce a higher quality result.

To suggest we shouldn’t write good markup and good CSS because some team members can’t understand basic HTML structures and semantics is a cop-out.

I don't recall seeing styling techniques that recommend bad markup. Class based CSS architectures are meant to complement semantic HTML, not act as a replacement. Anyone using classes as a replacement for semantic HTML is making a poor choice.

Otherwise, we’d still be building sites in tables because CSS layout is supposedly hard for inexperienced developers to understand.

Let me tell you about the state of HTML emails… 😛

You’d want to create custom classes most often for a few cases:

  • When there are not existing elements, attributes, or standardized data structures you can use…
  • When you wish to arbitrarily group differing markup into one visual style…
  • You’re building it as a utility mixin.

This comes off as moderately dogmatic. You can use classes as a way of saying "this is a piece of a design" or "here is a reusable chunk of style code that will perform a specialized function".

Another concern might be building up giant stacks of selectors. In some cases, building a wrapper class might be helpful, but generally speaking, you shouldn’t have a big stack of selectors because the elements themselves are semantically different elements and should not be sharing all that many styles.

Sometimes you have an article (article), and the article has a heading (article h1).

Sometimes you have a section (section), and the section has a heading (section h1).

Sometimes the articles have sections, and sometimes the sections have articles.

With the "semantic CSS" concept, this becomes a difficult problem to solve without using child selectors, which might require a selector along the lines of article > header > a > h1.

Saying "you shouldn't have a big stack of selectors" ignores the reality that styling based on semantics will inevitably lead to a big stack of selectors for large projects that have more varieties of content structures.

It’s time to up our CSS game. We can remain dogmatically attached to patterns developed in a time and place we have left behind, or we can move forward with CSS and markup that correspond to defined specs and standards. We can use real objects now, instead of creating abstract representations of them. The browser support is there. The standards and references are in place. We can start today. Only habit is stopping us.

As a developer I have spent years maintaining large projects that were written by numerous different organizations using different techniques for styling.

Projects where styles were based solely on specific semantic elements or attributes had more problems. They took longer to fix. And, worst of all, were most likely to have side-effects triggered by changes.

That is anecdotal, and I will present zero data to back up my claims.

Now it's time for me to do a 180°.

After all of that breaking down and disagreeing and nit-picking, it's important for me to emphasize this:

Tim Baxter is not wrong.

The majority of points made have sound backing, and good reasoning behind them. Achieving a simplification of style and reduction of code is an admirable goal.

Styles that are based on the default semantics of HTML elements are very useful, and have a place in large scale projects.

I'm going to add a few counter-counter-points to make the case for styles based on semantics. I hope to emphasize the point that I believe there is a middle ground that can be reached, and encourage everyone to avoid taking a purely dogmatic approach in favor of balance.

Good Defaults

It's of the utmost importance that all elements in projects have good default styles. Large and small alike.

If your default styles aren't useful, you will write more repetitive CSS every time you need to override the defaults.

If your default styles aren't useful, your content editors will tire of having to constantly waste their time "fixing" their markup.

If your default styles aren't useful, you increase the chance of future maintainers making mistakes.

Good defaults can be for more than just element selectors. If you know that the majority of your tabbed UI components will follow a specific pattern, it absolutely makes sense to tie those styles to [role="tab"].

The tradeoff is that you will then have to double check all overrides if the defaults ever change. My recommendation is to keep defaults geared toward structure using:

  • floats
  • margins
  • padding
  • position
  • display
  • box-sizing

and let classes do the theming work with colors, background images, border radii and the like.

Modular Content

The last few years have seen a big push in a concept of modular content. Modular content is used to allow pages to not all follow a single cookie-cutter template. In the past where you might have a CMS that had a few different templates:

Template A

  • header
  • carousel
  • intro copy
  • call-to-action modules
  • body copy
  • footer

Template B

  • header
  • intro copy
  • accordion
  • body copy
  • footer

…with modular content, each content type can be added as structured content in any order. Instead of two templates with rigid structures you could have a single page with regions:


  • header
  • content
  • footer

…and then a variety of structured content types that can be added to the page:

  • rich text
  • accordion
  • carousel
  • call-to-action
  • quote

With this shift in development practices, different types of content lend themselves for different styling techniques.

When the structure is being generated by a template, such as a carousel or an accordion, using many classes and attributes to style the content will have no impact on the content editor. The editor would be editing structured fields, such as a "heading" field and a "content" field for each accordion item.

When the structure is free-form, such as with rich text, using styles based on semantics and the occasional utility class or attribute will go a long way to simplifying the content entry process.

In Conclusion

I think it's important to regularly question best practices and see if there is room for improvement. Tim Baxter's article does a good job at examining current common practices and suggesting alternative approaches.

I can only hope that this response will further encourage such critial thinking.