Illustration by

Meaningful CSS: Style Like You Mean It

These days, we have a world of meaningful markup at our fingertips. HTML5 introduced a lavish new set of semantically meaningful elements and attributes, ARIA defined an entire additional platform to describe a rich internet, and microformats stepped in to provide still more standardized, nuanced concepts. It’s a golden age for rich, meaningful markup.

Article Continues Below

Yet our markup too often remains a tangle of divs, and our CSS is a morass of classes that bear little relationship to those divs. 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. 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.

We were warned about these patterns years ago:

In a site afflicted by classitis, every blessed tag breaks out in its own swollen, blotchy class. Classitis is the measles of markup, obscuring meaning as it adds needless weight to every page.

Jeffrey Zeldman, Designing with Web Standards, 1st ed.

Along the same lines, the W3C weighed in with:

CSS gives so much power to the “class” attribute, that authors could conceivably design their own “document language” based on elements with almost no associated presentation (such as DIV and SPAN in HTML) and assigning style information through the “class” attribute… Authors should avoid this practice since the structural elements of a document language often have recognized and accepted meanings and author-defined classes may not. (emphasis mine)

So why, exactly, does our CSS abuse classes so mercilessly, and why do we litter our markup with author-defined classes? Why can’t our CSS be as semantic and meaningful as our markup? Why can’t both be more semantic and meaningful, moving forward in tandem?

Building better objects#section2

A long time ago, as we emerged from the early days of CSS and began building increasingly larger sites and systems, we struggled to develop some sound conventions to wrangle our ever-growing CSS files. Out of that mess came object-oriented CSS.

Our systems for safely building complex, reusable components created a metastasizing classitis problem—to the point where our markup today is too often written in the service of our CSS, instead of the other way around. If we try to write semantic, accessible markup, we’re still forced to tack on author-defined meanings to satisfy our CSS. Both our markup and our CSS reflect a time when we could only define objects with what we had: divs and classes. When in doubt, add more of both. It was safer, especially for older browsers, so we oriented around the most generic objects we could find.

Today, we can move beyond that. We can define better objects. 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. We can define the elephant instead of saying things like .pillar and .waterspout.

Clearing a few things up#section3

But before we turn to defining better objects, let’s back up a bit and talk about what’s wrong with our objects today, with a little help from cartoonist Gary Larson.

Larson once drew a Far Side cartoon in which a man carries around paint and marks everything he sees. “Door” drips across his front door, “Tree” marks his tree, and his cat is clearly labelled “Cat”. Satisfied, the man says, “That should clear a few things up.”

We are all Larson’s label-happy man. 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? You can’t have more than one main element, so you already know how to reference it directly. The new elements in HTML5 are nearly a decade old now. We have no excuse for not using them well. We have no excuse for not expecting our fellow developers to know and understand them.

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

An end-user may not notice or care if you stick a form class on your form element, but you should. You should care about bloating your markup and slowing down the user experience. You should care about readability. 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. “Why should I care” was the death rattle of those advocating for table-based layouts, too.

Start semantic#section4

The first step to semantic, meaningful CSS 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.

If and when the semantics of HTML5 fall short, we have ARIA, specifically designed to fill in the gaps. ARIA is too often dismissed as “just accessibility,” but really—true to its name—it’s about Accessible Rich Internet Applications. Which means it’s chock-full of expanded semantics.

For example, if you want to define a top-of-page header, you could create your own .page-header class, which would carry no real meaning. You could use a header element, but since you can have more than one header element, that’s probably not going to work. But ARIA’s [role=banner] is already there in the spec, definitively saying, “This is a top-of-page header.”

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.

And it’s not just about those big top-level landmark elements, either. ARIA provides a way to semantically note small, atomic-level elements like alerts, too.

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. Instead, use [role=button] on elements that should look and behave like buttons, and style accordingly:

[role=button] {

Anything marked as semantically matching a button will also get the same styles. By leveraging semantic markup, our CSS clearly incorporates elements based on their intended usage, not arbitrary groupings. By leveraging semantic markup, our components remain reusable. Good markup does not change from project to project.

Okay, but why?


  • If you’re writing semantic, accessible markup already, then you dramatically reduce bloat and get cleaner, leaner, and more lightweight markup. It becomes easier for humans to read and will—in most cases—be faster to load and parse. You remove your author-defined detritus and leave the browser with known elements. Every element is there for a reason and provides meaning.
  • On the other hand, if you’re currently wrangling div-and-class soup, then you score a major improvement in accessibility, because you’re now leveraging roles and markup that help assistive technologies. In addition, you standardize markup patterns, making repeating them easier and more consistent.
  • You’re strongly encouraging a consistent visual language of reusable elements. A consistent visual language is key to a satisfactory user experience, and you’ll make your designers happy as you avoid uncanny-valley situations in which elements look mostly but not completely alike, or work slightly differently. Instead, if it looks like a duck and quacks like a duck, you’re ensuring it is, in fact, a duck, rather than a
  • There’s no context-switching between CSS and HTML, because each is clearly describing what it’s doing according to a standards-based language.
  • You’ll have more consistent markup patterns, because the right way is clear and simple, and the wrong way is harder.
  • You don’t have to think of names nearly as much. Let the specs be your guide.
  • It allows you to decouple from the CSS framework du jour.

Here’s another, more interesting scenario. Typical form markup might look something like this (or worse):

<form class="form" method="POST" action=".">
	<div class="form-group">
		<label for="id-name-field">What’s Your Name</label>
		<input type="text" class="form-control text-input" name="name-field" id="id-name-field" />
	<div class="form-group">
		<input type="submit" class="btn btn-primary" value="Enter" />

And then in the CSS, you’d see styles attached to all those classes. So we have a stack of classes describing that this is a form and that it has a couple of inputs in it. Then we add two classes to say that the button that submits this form is a button, and represents the primary action one can take with this form.

Common vs. optimal form markup
What you’ve been using What you could use instead Why
.form form Most of your forms will—or at least should—follow consistent design patterns. Save additional identifiers for those that don’t. Have faith in your design patterns.
.form-group form > p or fieldset > p The W3C recommends paragraph tags for wrapping form elements. This is a predictable, recommended pattern for wrapping form elements.
.form-control or .text-input [type=text] You already know it’s a text input.
.btn and .btn-primary or .text-input [type=submit] Submitting the form is inherently the primary action.
Some common vs. more optimal form markup patterns

In light of all that, here’s the new, improved markup.

<form method="POST" action=".">
		<label for="id-name-field">What’s Your Name</label>
		<input type="text" name="name-field" id="id-name-field" />
		<button type="submit">Enter</button>

The functionality is exactly the same.

Or consider this CSS. You should be able to see exactly what it’s describing and exactly what it’s doing:

[role=tab] {
	display: inline-block;
[role=tab][aria-selected=true] {
	background: tomato;

[role=tabpanel] {
	display: none;
[role=tabpanel][aria-expanded=true] {
	display: block;

Note that [aria-hidden] is more semantic than a utility .hide class, and could also be used here, but aria-expanded seems more appropriate. Neither necessarily needs to be tied to tabpanels, either.

In some cases, you’ll find no element or attribute in the spec that suits your needs. This is the exact problem that microformats and microdata were designed to solve, so you can often press them into service. Again, you’re retaining a standardized, semantic markup and having your CSS reflect that.

At first glance, it might seem like this would fail in the exact scenario that CSS naming structures were built to suit best: large projects, large teams. This is not necessarily the case. CSS class-naming patterns place rigid demands on the markup that must be followed. In other words, the CSS dictates the final HTML. The significant difference is that with a meaningful CSS technique, the styles reflect the markup rather than the other way around. One is not inherently more or less scalable. Both come with expectations.

One possible argument might be that ensuring all team members understand the correct markup patterns will be too hard. On the other hand, 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. If nothing else, the patterns a team follows will be clear, established, well documented by the spec itself, and repeatable. Good markup and good CSS, reinforcing each other.

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. Our industry can—and should—expect better. Otherwise, we’d still be building sites in tables because CSS layout is supposedly hard for inexperienced developers to understand. It’s an embarrassing argument.

Probably the hardest part of meaningful CSS is understanding when classes remain helpful and desirable. The goal is to use classes as they were intended to be used: as arbitrary groupings of elements. 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. In some cases, you might truly have an object that the HTML spec, ARIA, and microformats all never accounted for. It shouldn’t happen often, but it is possible. Just be sure you’re not sticking a horn on a horse when you’re defining .unicorn.
  • When you wish to arbitrarily group differing markup into one visual style. In this example, you want objects that are not the same to look like they are. In most cases, they should probably be the same, semantically, but you may have valid reasons for wanting to differentiate them.
  • You’re building it as a utility mixin.

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. The point of meaningful CSS is that you know from your CSS that that button or [role=button] applies to all buttons, but [type=submit] is always the primary action item on the form.

We have so many more powerful attributes at our disposal today that we shouldn’t need big stacks of selectors. To have them would indicate sloppy thinking about what things truly are and how they are intended to be used within the overall system.

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.

About the Author

Tim Baxter

Tim Baxter is a designer, developer, content wrangler, product manager, or strategist, depending on which hat he happens to be wearing on any given day. He can often be found poking around in the Django community, on Github, or on Twitter.

63 Reader Comments

  1. Really interesting article. I love the idea of using ARIA roles/attributes over custom classes wherever possible: seems so obvious, and yet I haven’t on the whole been doing that – at least with any consistency – until now.

    What remains unclear to me is how to retain the neat namespacing that an approach like, eg. BEM provides (which leads to lots of long, ugly class names, but which also has significant advantages in terms of being able to write modular CSS). Any thoughts on this at all?

  2. Very good food for thought, and bound to draw its share of controversy. I can see that, in some cases, this would lead to more maintainable CSS, and in some cases, it would lead to more maintenance difficulties.

    What do you consider layout classes to be? Utility classes? How about the ol’ standard OOCSS example, .media?

    I’d personally add at least one other valid case for classes: stylistic variations (‘modifiers’ in BEM parlance), such as button.cta or ul.compact. Is that what you mean by this case?

    When there are not existing elements, attributes, or standardized data structures you can use.

  3. Love the article.
    Spot on.
    Is snippet #3 missing the closing ??

  4. Some of the points made are important to consider, but I would disagree with the practice of your css styling elements directly. I have a goal with my css, where I try to make classes work in isolation.

    To explain, there will be times where you need an element such as a form or submit button differently. If you have styled root elements such as ‘form’ or properties like ‘[type=submit]’, then you will need to create other classes that override a bunch of properties found in the root element. Then if the root element styling changes, your ‘child’ classes all need to be updated also. This is problematic for maintenance.

  5. What remains unclear to me is how to retain the neat namespacing that an approach like, eg. BEM provides… Any thoughts on this at all?

    I think HTML itself provides an awful lot of namespacing opportunities, and inherently tends to follow its own block element modifier sort of logic. For example, a `<form>` is already block, `<input>` are literally elements within the form block, and they all have intrinsic attribute modifiers. You can leverage these pieces as flat or as nested as you need to.

    stylistic variations (‘modifiers’ in BEM parlance), such as button.cta or ul.compact. Is that what you mean by this case?

    Absolutely. I would suggest that in many cases, that [type=submit] would be your call to action, but yes, there will be cases in which your default opinions do not apply and there are no existing semantics on which to lean. In those cases, by all means, define your own terms. It’s just a matter of defining the special cases as actually being special.

  6. Why would I use classes vs HTML5 elements? First, to me it brings better versatility and backwards compatibility. For example if I have WordPress theme and would want to change the structure a bit, I would need to add many CSS rules to keep existing websites from breaking. If I use classes instead, most likely I will not need any backward compatibility rules.

    So I guess we can use HTML elements for main structure points and classes for smaller elements where needed.

    Browser compatibility might be one of the cases of double tagging like MAIN CLASS=”MAIN”, especially if your code is used in governmental or healthcare sector.

  7. I must confess I feel a sudden guild of not upping my HTML-game sooner… I loved the article but could you reflect on the following?

    1) CSS-performance: I never considered -until now- using “qualified attribute-selectors with value” like [role=button] because of its performance. Classitis has it’s own performance issues. So don’t we trade in selector-performance against classitis? Or do you consider the one more important then the other?

    2) Working in a Drupal-world, things get thrown around a lot semantically. I couldn’t agree more on how important it is to get the HTML-semantics right. But I do secretly sleep a bit better at night when I know the theming is only class-based. Naming conventions like BEM no longer depends on a specific html-structure to be sustained throughout the project. Won’t we be sacrificing maintainability by moving away from class-based approaches?

  8. @Mathieuspil Also we have to remember that best practices of using ARIA selectors have tendency to change. So one day you might need to change CSS if you need to adjust the ARIA markup. So we come back to maintainability question…

  9. CSS-performance: I never considered -until now- using “qualified attribute-selectors with value” like [role=button] because of its performance. Classitis has it’s own performance issues. So don’t we trade in selector-performance against classitis?

    Mathieuspil, I think if your performance bottleneck is CSS selectors, you are in a very, very good place. I find it far more common to have bloated inefficient DOMs creating much greater slowdowns.

    Naming conventions like BEM no longer depends on a specific html-structure to be sustained throughout the project. Won’t we be sacrificing maintainability by moving away from class-based approaches?

    BEM depends on a certain specific selector structure being always in place and sustained throughout the project, no matter how awful the markup may be. There are relatively few ways to mark up something well. Do it well, and style that. The CSS Zen Garden showed us years ago how to style in different ways while keeping the same semantic markup.

  10. I think this article is missing a few points:

    – Separation of concern: semantics (and structure) may change while styling may not. For this reason, tying the styling to HTML is bad regarding maintenance.

    For example: using “form > p or fieldset > p” would prevent authors from replacing a `p` with a `div` (a very strong possibility) without editing the style sheet.

    – Since you mention HTML bloat, why not pointing out that using “[type=text]” instead of “.text-input” will lead to more selectors in the styles sheet (url, number, tel, email, etc.). A class being more generic can target different/more elements.

    – In the “Common vs. optimal form markup” section, you say people should use `form` instead of `.form`. While I agree that using `[role=button]` is better than using `.button` (as long as those elements are truly acting like buttons, i.e. they are actionable via space bar), I would warn people about what this means in regard to the cascade since `form` (type) is less specific than `.form` (class). In my opinion/experience, it is better to write rules with the same specificity “range”.

  11. Reusing classes on a different markup is really performant in cross project reusability. Defining things globally never worked out in the wild since a lot of the time we need to override the default styling for various reasons such as multiple states of a single form. I really appreciate the clean approch but building scalable interfaces is all about micro chunks of content working together in an orderly fashion, not reusing larger globally defined components. A simple form in reality is actually lot more than a couple of labels and inputs. Still a lot of good points were made in the article.

  12. Hey, it’s 2016. No, people don’t want to style in different ways while keeping the same semantic markup anymore. Now they want to style the same on different semantic markup.

  13. TL;DR; Attribute selectors like [role=button] doesn’t have support in older browsers and that is the reason, why classes are still used as a main tool to style elements -> and it surely will do for another few years.

    You must understand that semantics is like categories for things in our native language. It shouldn’t serve another purpose. For example even though there are all cars, and buildings, and phones. It says nothing about it’s color, size, material etc. You will always add another word before the “category” eg. Blue Car -> and that word is a class that describes its appearance.

    You can have “global” tag, but all cars doesn’t look the same. So you end up with blue selector or “.blue-cars car” selector -> yeah, chaining and nesting, the thing all the BEMs and OOCSS tried to solve because of no namespacing option for CSS.

    You’ve solved nothing with your “form” vs. “.form”, you’ve just introduced inconsistent syntax and possibly created opportunity for chaining.

    The main idea of this article should not be to style tags, but to use better class names, that make sense.

    I am big fan of attribute selectors specifically AMCSS modules that can bring some kind of containment which CSS currently lacks.
    ( )

    My advice for styling tags is simple. Don’t style them directly! (except for normalize).

  14. this approach is simply unsustainable on a large project. Trust me, I’ve tried. I work with the CSS on one of the oldest commercial sites on the web, and when we’re re-platforming and re-skinning, we have to write new styles and markup that lives side-by-side with legacy. This approach does not support what we’re doing.

    I really like the philosophy here though, and would definitely recommend it for smaller sites or projects.

  15. I had a very long comment that was somehow deleted in the the submission process…

    However, the gist of it was that I think you bring up a lot of good points, but that the real answer to the problem you are proposing a solution to isn’t to neglect classes all together, but to instead write classes with meaningful names (BEM syntax?) and generally know when it’s proper to write CSS utilizing appropriate semantic hooks, whether those be based on mark-up or attribute selectors (native to HTML or ARIA).

  16. Food for thought. Most of my concerns are already mentioned, but here goes:

    – When you suddenly discover you need e.g. a button element that is unstyled/very differently styled, then you suddenly have to struggle with resetting styles.
    – Selectors like form > p, button and [role=button] all have different specificity. One of the advantages of BEM is that most selectors are the same specificity, giving a more predictable cascade.
    – However, I agree that careful use of ARIA attribute selectors promote good markup and probably should be considered more often, but only when the risk of needing to override styles is also considered.

  17. I have a few concerns about this approach falling short under non-simplistic development scenarios.

    Using html tags to apply visual styles can be restrictive, especially in larger (real life) systems.

    A simple example would be a data entry page with a search option in the header. Two forms, matching markup, different visual styles. Applying styles to the ‘form’ creates a coupling between the semantics of the content and the visual presentation.

    In simpler systems this could work but in more complex scenarios it’s worth considering alternatives that explore patterns in how content is displayed.

    Your markup might look busier with more classes but the separation of concerns will make for fewer surprises when someone tweaks how a form should look.

    It’s also forward facing, with the impending increase in component based design and development, sharing content patterns across components without making assumptions about the markup used.

    Also, whilst it may not be a priority, there are concerns over the performance of attribute selectors in CSS so I’m not sure but the ARIA label approach could cause problems.

  18. For example: using “form > p or fieldset > p” would prevent authors from replacing a `p` with a `div` (a very strong possibility) without editing the style sheet.

    It may be a strong possibility, but the W3C clearly states it should be a `p`, not a `div`, so it would be an undesirable change, wouldn’t it? There are relatively few ways to mark up a given thing well. Make it easy and clear to do the right thing, and harder to do the incorrect thing.

    Since you mention HTML bloat, why not pointing out that using “[type=text]” instead of “.text-input” will lead to more selectors in the styles sheet (url, number, tel, email, etc.). A class being more generic can target different/more elements.

    It can, but it doesn’t necessarily. No matter what system one uses, it’s up to prudent developers to manage bloat — and specificity, for that matter. It will go wrong if left unattended. BEM, for example, can quickly bloat both markup and styles with overly specific Blocks and Elements that are not reusable. Personally, I’ve found less bloat in both markup and CSS using these methods, but there is not methodology that absolves you from vigilance.

    Defining things globally never worked out in the wild since a lot of the time we need to override the default styling for various reasons such as multiple states of a single form.

    No one is saying you cannot override, or style for multiple states. You do, however, have much richer tools at your disposal for doing so than simple and arbitrary classes. You can, instead, use the actual attributes of the object. If it’s disabled, for example, then it is disabled. Adding a class marking it .disabled doesn’t change that or add anything to it.

    TL;DR; Attribute selectors like [role=button] doesn’t have support in older browsers and that is the reason, why classes are still used as a main tool to style elements -> and it surely will do for another few years.

    Agreed, but today the toolset is much improved. And it has been for awhile. You can safely use attribute selectors in all major browsers, including IE8 and above.

    When you suddenly discover you need e.g. a button element that is unstyled/very differently styled, then you suddenly have to struggle with resetting styles.

    There is no more struggle than if your starting point had been .button. But you will have defined what makes this button special in the first place.

    Using html tags to apply visual styles can be restrictive, especially in larger (real life) systems. 
    A simple example would be a data entry page with a search option in the header.  Two forms, matching markup, different visual styles.  Applying styles to the ‘form’ creates a coupling between the semantics of the content and the visual presentation. 

    There’s no restriction. Using your example, I would suggest that the search form could (and should) be marked up with [role=search]. But to address the larger point of “what happens when a form is different”, I would say mark it up and style it as appropriate. At some point, markup alone will not be enough to differentiate. And ARIA will not not have the additional semantics you need. And your form will not follow any micro formats. On a long enough timeline, you will have a form that truly is different just because and is not different for any semantic, structural or standardized reason. And when that day comes, I would say by all means, create form.just-because and define your own internal standard. But I would suggest knowing and leveraging the established standards first.

  19. “A word of caution: don’t throw ARIA roles on elements that already have the same semantics. So for example, don’t write

  20. The W3C recommends paragraph tags for wrapping form elements. This is a predictable, recommended pattern for wrapping form elements.

    The markup in the document is non-normative. It’s not a recommendation – it’s there to serve as an illustration.

  21. Completely agree with the problem you mentioned about too many

    s. Finding closing

    in the forest full of

    s is quite painful.

    However, the proposed solution in this article will cause more problems later on in the project as many people pointed out.

    I am also looking for the answer to this problem and couldn’t find perfect answer yet. Your solution about using role=”…” is quite interesting though 🙂

  22. I’m not sure everyone in the comments is willing to consider the proposal of this post.

    BEM or any other CSS architecture advocates for separating styles from content. This article is not arguing that you must use styles and HTML together. It is arguing that you start with the most “correct” HTML and then style it.

    When it comes to styling you can override my-form__my-submit--primary or button[type=submit].

    Styles will always be coupled to something. It could be coupled to the HTML and I override the styles via element selectors. Or I couple my styles to my specific CSS naming conventions. In either scenario, if I need something unique I’m going to override existing styles.

    The advantage of the approach prescribed here is that you’ll have good markup to move forward with.

  23. The example isn’t realistic. Take button for example, you still have .btn-secondary .btn-success .btn-warning .btn-danger to take care with.

    So your suggestion just creates a special case in style sheet.

  24. Wow, I didn’t realize there would be so much resistance to an idea like this. We’re implementing a lot of it, in what is going to be thousands of complex sites, with very little trouble. It feels to me, like a lot of these comments are similar to the comments you might have seen during the movement away from tables for layout… Actually, I still see arguments for tables for layout occasionally… weird, I know…

  25. A nice idea but impracticable except on small sites. Because of the specificity problems it creates, and because of the maintainability problems it creates in the markup, as already noted. The resistance to this idea has nothing to do with kneejerk conservatism. It comes from countless hours of hard experience by people who understand the semantic imperative, who want to do the right thing, but who recognize that it is better to make a small compromise in order to keep their sanity. A tiny compromise really, because CSS preprocessors allow completely semantic class-naming.

  26. @philwills No one is arguing that writing semantic HTML is a bad idea. The issue here is the scalability of attaching all or most of your styling to the HTML elements or attributes of those elements. Like many have said, not a big issue for small sites, but not maintainable for large/complex sites.

  27. mikeriley, why is not maintainable?

    On a large site, wouldn’t predictable, tested and documented markup patterns provide even more value than on a small site?

    Wouldn’t a large site be even more likely to have a design guide already dictating things like how all form submission buttons should look like?

    When CSS layout first hit, many people said it wasn’t feasible for large sites. But it was. When RWD hit, people said it wasn’t feasible for large sites, but it was.

    A few years ago, if you had said this wasn’t feasible, I would have agreed. The markup simply wasn’t expressive enough. But today it is.

  28. I have to agree with the OOCSS community feedback on this one. I like the idea of performance gains, but I don’t think the rubber will hit the road with large projects where numerous are developers contributing, that are already using Bootstrap, and other naming styleguides like MVCSS ( I also don’t think the performance gains will have an impact when looking at the overall picture of things that impact performance in modern projects.

    Separation of concerns are essential, and we have things like this being taught by industry experts:

    – Only bind CSS to CSS-based classes:
    .im-a-css-class {

    – Don’t write DOM-like selectors:
    header nav ul li a {

    – Don’t bind CSS onto data-* attributes:

    – Don’t bind JS onto CSS classes:

    – Use JS classes:


  29. Nice idea, but but my concern is that at some point you will end with a data/aria attributes bloat in order to style precisely some custom elements (especially if the design is not completely consistent)
    Other concern is that you are using semantic attributes for style hooking, which couples a lot the markup and the style. If more meaningful class names are used and a semantic markup, you should be ok.

  30. @Tim Baxter, it’s not maintainable because you are tightly coupling your HTML semantics with the visual presentation.

    Please read:

    You clearly violated most of the points made in that article. The W3C has not “built” large scale, cross-browser websites. Semantics have to change – no matter what you end up with. Div spaghetti is inevitable because of the various cross-browser solutions to common problems, but classes remain agnostic to the HTML semantics.

    Classes don’t make your HTML harder to reason about, they can make it easier by using more meaningful class names, they can be used to describe the div spaghetti.

    For performance reasons, avoid child selectors like “form > p”, classes help resolve this.

    I can go on and on about why classes trump semantic selectors.

    Also, Regression is a hell of a drug.

    Remember this, HTML can and will change, but classes can remain frozen in time and still present the same old visual you expected.

  31. Inspired by ‘s normalise css, I made a bootstrap-like setup with three css files:
    1. A classless stylesheet which normalises and styles all the HTML elements. This was so that we could build prototypes quickly across a very large public sector organisation with lots of developer teams and still have consistency. This sheet is sacred.
    2. A “classes” stylesheet for when you need to override the default styling set in the default stylesheet.
    3. A “molecules” stylesheet where I placed the styles for re-usable HTML components. Here the parent wrapper gets a class but the children (as much as I can avoid) remain classes.

    Sometimes a fourth sheet is added for custom elements which will not be used very often in other projects. This overrides the other sheets. I know it’s not optimal to override but it means we can keep the other three sheets clean and relevant.

  32. I love some of the things that this article promotes – we should definitely all be striving to create more meaningful semantic markup rather than div soup and using roles for certain use cases in styling makes a lot of sense.

    But I don’t agree with the argument that we should be throwing away hugely useful conventions such as naming schemes and CSS methodologies – ideas that have evolved and grown over years to help tackle many of the issues when authoring CSS on a larger scale.

    The main point that I think the article misses when comparing this approach to using class naming schemes is specificity and how much of a pain point this can be on sites of scale. The reason a naming scheme is so useful is that it keeps the specificity as low as possible, meaning it is much easier to override styles when needed. It also means you can build up components using semantic classnames, so should that structure change in the future, the styling isn’t so tightly tied to the markup and is much easier to update.

    On a simple site this isn’t as much of an issue, but on large platforms this becomes very important.

    One of the biggest goals for me when writing CSS is to separate my concerns – the more that you hook CSS into HTML semantics and structure, the harder it becomes to change one without also changing the other. This doesn’t mean that you can’t still write meaningful markup with considered semantics, which I think is the articles most valuable point.

  33. This article completely ignores CSS specificity, which fights against code reuse and modularity and is the reason why we’ve developed these class-heavy methodologies in the first place.

    Following the recommendations of this article would result in overly lengthy selectors and a tangled mess of specificity overrides in your CSS.

    The author should seek first to understand.

    (By the way, the comments system here is broken.)

  34. This completely overlooks specificity, code written this way would be a total nightmare to maintain.

    Please no one do this.

  35. As I understand it, the thesis here is that HTML + ARIA now has enough semantics to describe the presentation/UI of any web product.

    This just isn’t true. Let’s look at some examples.

    First, it’s pretty common to have a variety of ways of presenting homogenous listings of, say, articles. Google’s Material Design system, for instance, has Lists, and Grid Lists. The first is optimized for textual content, where any icon or other image is secondary. The Grid List, by contrast, is optimized for content where the image element is the most important (e.g. products, or albums). AFAIK, neither HTML nor ARIA provides “native” semantics to distinguish these two objects. There is little need to, from the standpoint of what HTML and ARIA are meant to do. Yet from a user experience point of view this distinction is a good one, and is definitely not arbitrary.

    Let’s look at another example. Say I have a main navigation. Let’s mark it up following this article’s advice:

          <li><a href="/">Home</a><li>
          <li><a href="#search-pane">Search</a></li>
          <li><a href="/orders">Orders</a></li>
          <!-- etc -->
      nav {}
      nav ul {}
      nav li {}
      nav a {}

    Now, let’s say I also have a secondary nav, for logging in and other user account actions. It shouldn’t look the same (let’s assume the main nav is a vertical sidebar, and this is a horizontal strip at screen top-right). If it’s part of a <header role="banner">, I can use a descendant selector:

      [role="banner"] nav {}
      [role="banner"] nav ul {}
      [role="banner"] nav li {}
      [role="banner"] nav a {}

    Unfortunately, since the main nav styles also apply here, I will need to override any that conflict with what I want for this secondary navigation. Even worse, any time anyone changes (or adds to) the styles for the main nav, they need to know to check the secondary nav to make sure that hasn’t been negatively affected. This is the opposite of separation of concerns, and is perhaps the main reason approaches like OOCSS were invented.

    I could come up with a lot of other real, practica examples. Note also that neither of these would be isolated to “large” sites or apps – every developer faces these challenges.

    In my mind, the following CSS is actually more meaningful (i.e. semantic) than the strings of more generic selectors above, because the speak the language of the product itself:

      .secondary-nav {}
      .secondary-nav__items {}
      .secondary-nav__item {}
      .secondary-nav__action {}

    As other commenters have noted, this approach improves robustness of the code and makes it more maintainable, since I can add a <button type="button"> to the secondary nav (if that’s what the best semantics are) and have it styled like the other actions just by adding a class. I can then refactor it to be an anchor with a fragment href later if that turns out to be better, and I don’t need to track down and change the CSS again. Likewise, if I need a div wrapper somewhere (say, to support some new dynamic behavior), I can add that with less risk of breaking a child selector somewhere.

    The best thing is that these modular and mostly class-based approaches don’t prevent us from writing beautiful, semantic markup and using ARIA appropriately. Good front-end development requires that, period. Now, many many designers and devs could do much better at learning the basics of HTML and ARIA, I’ll grant that. I’d strongly encourage it to boot. But robust, modular UI code builds on the standards of HTML and ARIA and does not mistakenly believe they are sufficient on their own.

    The current community movement is towards the idea of well-considered design systems embodied in reusable UI components that encapsulate markup, style and behavior (this approach needn’t sacrifice progressive enhancement, either). This is not a world of endless selector chains, specificity conflict and unforseen side effects. Preprocessors and other tools have made it possible to have a single representation of a component, obviating the need to change a class in hundreds of instances. The best systems come out of solid product goals and design patterns, and use class selectors (among others) to make codebases full of meaning. There is no need to worry so much about classitis or divitis: its time as a source of problems has passed. Semantic markup, ARIA and modern UI development can be friends! Isn’t that a good thing?

  36. Has there been any discussion of how we might transition to a more useful css specificity model? Perhaps something like was done with box-sizing: border-box. Then we could have both clean html AND clean css.

  37. Ryan Frederick hits the nail on the head – and so does Tim Baxter, each in their own ways.

    The fact is, a combination of *both* approaches is required for efficient, semantic and maintainable code.

    As developers, we should always be looking at reducing clutter in HTML – and it’s never been easier to do so!

    We have a plethora of meaningful tags and attributes at our disposal – and we should learn how to use all of them.

    We also need to learn to encapsulate common design patterns and to avoid nested selectors wherever feasible. Classes make this far easier to achieve.

    As for avoiding div soup, creative use of pseudo-classes can result in a much clearer separation of semantic markup and style.

  38. On a large site, wouldn’t predictable, tested and documented markup patterns provide even more value than on a small site?

    — Tim Baxter on Meaningful CSS: Style Like You Mean It

    Yes! And especially in the case of accessibility & interactive widgets, I couldn’t agree more.

    Predictable markup patterns mean a predictable experience for users of assistive technology such as screen readers. Take a menu widget for example. If you allow the developer infinite ways to markup a menu, you are paving the way for inconsistencies in how your menus are described to screen reader users.

    What our users want is for the semantics of the menu to be exactly the same, for every menu, no matter how it has been visually styled. Markup patterns form a solid foundation in terms of achieving this. And it’s incredible how much CSS mileage you can get out of a fixed markup pattern these days. I have a standard pattern for most widgets and have not found a need to change them, even when the visual design changes. I like to think of them as my mini Zen Gardens 🙂

  39. I consider thesis in this article idealistic and harmful.

    Basically it attracts people for the wrong reasons. The main assumption here is that it is possible to create highly coherent front end code base, that will even sustain itself, get stronger thanks to inherent forces of meaning and sense. This is highly attractive notion, as peaple strive for order, patterns and predictibility.

    But what is more harmful, the hidden implication here is that, there is sense connecting all of the mentioned technologies: ARIA, HTML, Microformats. We only need to discover it, embrace it.

    All od it is simply not true.

    You have to remember that front end environment is one of the most susceptible to change. It’s highly dynamic with no constraints on, why it shouldn’t be that way. It’s basically UI. If you start to force yourself to see in all of it high order meaning, you are doomed. It is beacuse HTML and ARIA have very basic ways of expressing content — very basic and thanks to that very flexible, composable etc. This is why tag

    can organize elements in form, but it also can group text.

    ARIA and HTML are good to express relations. They are not good display the content. And even with what they are good for it’s still really hard to use them, because of their hihgly general and versatile meanings. I mean… try to decide whether something should be or just . And trust, right now we are in teritory of interpretation, with very little clear rules to help us.

    This means that these elements are highly composable. If you try to force on them predetermined structure, you already lost. It is because you assume that there will be content that can be split into similar objects, possible to express them in the same way. And that simply won’t happen. The possibilities of communication are to broad, to versatile.

    So what are we really talking about here? Think of it as an aspect of behavior. Something is <form> not because it has particular amount of input fields, not because it contains them grouped in particular way, not because of some ARIA elements, not even beacuse we use this particular name for it: ‘form’. Something is <form> because of the behavior of this element. It can look differently, but it will behave in the same way.

    And this is main part here: do not mix behavior with display. Don’t mix meaning with layout dynamics. If you start chasing this goal of meaningful CSS, you end up with messy CSS, very hard to maintain, that will break all the time.

    What’s worse, beacuse of very intensive coupling between the domains here, it will prevent you from creating more meaningfull HTML or ARIA elements. Imagine that. Imagine you created strict structure for particular component. You tied CSS with the structure. And after a while you found out, that you can improve accessibility or semantics of the component. In your current situation you cannot improve your codebase gradually. You cannot change HTML without touching CSS and figuring out what would be the best way to bind CSS with the new structure. How to again distribute layout and display responsibilities. HTML would seem to be untouchable. And you wouldn’t even start improving it piece by piece.

    Keep behavior and display separate. Make CSS composable to adjust it flexible nature of HTML.

    CSS is language for appareance, for layout interactions. It’s a visual enahncement of something that will still work without it. It should be treaded accordingly, as a separate domain, with interactions and relationships isolated from other domains as much as possible. BEM encourages it, but it is something of mental discipline, good organisation, and resolving highly complicated factors. Throwing into all of it also meaning? Why? There is no reason not to keep it separate. To me even such stupid classes like maring-left-20 is still practical. It’s very focused and very clear with it’s own responsibility. It’s bad beacuse of style, but style unfortunately can be amazingly not practical.

    And yes, use classes like ‘table’. Simply because this table is part of visual domain, not behavioral domain. It’s simple as that.

    And lastly, classitis argument is not really important. Whatever it takes to separate concerns, to split responsibilities, to ease out modifications — that is good. Classitis is very arbitrary way of judging something without no clear rules whatsoever.

    Clear you’re mind — separate concernes, focus interactions, simplify realtionships. Make your code composable and you will prosper in front end jungle.

  40. In my comment about widgets above I neglected to mention that I do use BEM too, so it’s not a completely class-free approach. I find that BEM helps strike a nice balance between class-itis and tight-coupling. So I guess this falls in line with Matthew’s comment above, which is “a combination of *both* approaches is required for efficient, semantic and maintainable code.”

  41. Interesting article which is no doubt going to cause disagreements. But anything that looks at better improving how we create our CSS and reduce HTML can only be a good thing.

  42. Why main class=“main”?

    It could be more complex, and sometimes I’ve even made more complex : main class=”main” id=”main” role=”main”

    The id is just to provide an anchor (for skip links, etc.).

    The role is here for some old browsers that do not provide semantic for main.

    Why a class? Consider that another template has to have a left column or whatever. The main tag could change its place, and everything is down if you style on it. 🙂

    I do agree: some HTML are really bloated and could be lighter, but if we started to have recommendations like “don’t rely on HTML structure”, it was to solve other problems 🙂

  43. This article has annoyed me a little.
    CSS architectures should be considered again for every project. I am not bias to using any architecture. I use lots of different architectures all the time, as they solve (or reduce) different sets of problems.
    If I tried to use a WordPress theme style architecture for the application I support at work, I’d be fired.

  44. Thank you! I’ve been trying to make this case for a while myself.

    I think writing everything in your stylesheets using BEM or SMACSS (or what have you), can create as many problems as it solves. Like all things, moderation. There’s no reason to create a `.className–whichWillNeverGetUsedAgain` to bypass global scope when a `.meaningful-parent-description elementName` pattern will do.

    Global scope has a purpose and can be very useful, and a lot of these classes end up being really unsemantic and bloat the markup.

    I find that using LESS’s @import (reference) and @extend together (or postcss-reference if you prefer PostCSS) a great way to set up component patterns and then abstract them out to meaningful, and simple selectors.

  45. Not getting into the merits here. Just want to remark this:

    Tim Baxter, both in the article and in comment 20, you say W3C states paragraph tags are recommended for wrapping form elements. In fact, as pointed out by Terrence in comment 24, the section you mention is non-normative.

    And I’d like to add that you linked to an old W3C work: HTML5 Editor’s Draft from 25 October 2010. The same example can be found on the most recent HTML5 Recommendation from 28 October 2014.

    Actually, the example in the current W3C HTML Editor’s Draft uses DIVs instead of paragraphs.

  46. Using attribute selector would definitely give some possibilities in the structure of CSS.

    But… Attribute doesn’t scale in performance. If you have 1000 different classes. The SCOM/DOM parsing time won’t differ if you have 10 or 10000 elements.

    This is not true for attribute selectors unfortunately. As they on an average site, could mean the parsing could take 10-100 times longer.

  47. This is a great article and expresses ideas that I’ve been trying to convey for some time, without ever being able to put it nearly so well.

    The real beauty of the approach is that it allow CSS selector specificity to work the way it was always designed to work. Instead of treating specificity as your enemy and fighting it in the way BEM tries to do, specificity becomes your friend, a jigsaw piece that fits naturally it to its place.

  48. You can’t be serious , really !
    This approach is what i tried back when browsers started to adopt css3 and miserably failed , it can raise so many maintenance difficulties , though it’s good for small ( really small ) projects , but even then personally i think it’s overkill 😐

  49. “In HTML, every element has a very specific, agreed-upon meaning […]”

    Funniest thing I read this week 🙂

  50. I keep seeing resistance to this concept and I honestly don’t get it. When you think of atomic design and the concepts behind OOCS and BEM consistent markup that can be composed into more complex components should go hand in hand. What I see most often is OOCS/BEM being used in place of good markup – essentially admitting defeat in that A) we have to deal with crappy code so put some lipstick on the pig B) we don’t really care about architecture or optimization, we just want it to look and work a certain way.

    Why can’t this simple concept be used with a variety of other best practices? For example I’d like to investigate how this might work in a React application with SASS and CSS-Modules.

    To me form follows function. So when people get frustrated with the concept that is suggested above by pointing out how BEM classes can be used across a variety of different tags, it makes me think “yeah, you could, but why?” Do I really need “btn” styling for button, input type=”button”, a, div, span, p… and on and on, or does that “btn” class infer a behavior too and that behavior is encapsulated by a specific HTML tag or ARIA role? So why not just change the tag or the role to align to behavior and get styling with it?

    The other thing I think about is consistency. While it’s great that designers can have 34 different ways they want to style a form – do they need 34 different ways? Just like there are 1272 different ways that the developer can code that form to get to one of the 34 different designs. Is it necessary? Does it provide value?

    Sometimes constraint breeds creativity. Reducing the number of variations, constrained by how we use markup + css, may force designers and developers down a path that produces more consistency and potentially better usability. This is why there needs to be deep discussion about architecture and architectural limitations. How do you architect a solution that takes a standards approach and provides both constraints and flexibility where they are needed?

    I think there is definitely a middle ground in this religious war. I’d really like to see more openness to talking through how the challenges can be overcome versus seeing any single solution as the ultimate “already fixed that” solution.

  51. I know I’m late but this article raise an interesting (controversial) points.

    If you really believe this would be the right way to design CSS, why don’t you (or others) create some sort framework or architecture or whatever you named it that proves or demonstrate this kind a thinking in practice.

    Because currently the alternatives is bootstrap or other CSS framework that has symptom of divitis and classitis.

Got something to say?

We have turned off comments, but you can see what folks had to say before we did so.

More from ALA