O'Reilly logo

Frontend Architecture for Design Systems by Micah Godbolt

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 4. HTML

One of the first challenges you’ll face as a frontend architect will be to tackle the markup you want your developers to be writing and your CMS to be producing. The Web doesn’t exist without HTML. Strip away your CSS and JavaScript and you will be left with raw content and controls.

Text, images, links, forms, and submit buttons: this is all the Web really needs, and it is the foundation of anything you’ll ever create on the Web. Start off with bad markup, and you’ll be writing bad CSS and bad JavaScript to make up for it. Start with great markup, and you’ll be able to write more scalable and maintainable CSS and JavaScript.

Markup of the Web’s Past

It wasn’t that many years ago that our approach to HTML markup was something quite similar to laying out a page in a brochure, magazine, or newspaper. It isn’t much of a surprise, though, as many of us came from a print background, or were working with designers or product owners with a print background. Before responsive design became the standard (and even some time after), most web properties were treated like a multipage print project. When the work was divvied up, you’d be assigned a page and you would start at the top and work your way down the DOM.

In the past, our markup typically fell into one of two camps: procedural or static. Let’s take a look.

Procedural Markup: 100% Automation, 0% Control

In the world of web publishing, it was pretty common for the frontend team to have little to no control over the markup. This was often due to feature development (which included HTML output) being done weeks or even months before the frontend team came onto the project. It was made worse by the fact that the origins of the markup were obfuscated by complex rendering processes, and did not come from a single template. This meant updating the markup was extremely difficult for anyone not familiar with the complexities of the CMS backend. And by that time, the backend developers had moved on to other tasks, and they rarely had time to go back and make any major changes to it.

The effect of this constraint is that CMSes and the backend developers would err on the side of too much markup and too many classes in order to give the “themer” proper hooks into the HTML. In the end, we’d get this:

<div id="header" class="clearfix">
  <div id="header-screen" class="clearfix">
    <div id="header-inner" class="container-12 clearfix">
      <div id="nav-header" role="navigation">
        <div class="region region-navigation">
          <div class="block block-system block-menu">
            <div class="block-inner">
              <div class="content">
                <ul class="menu">
                  <li class="first leaf">
                    <a href="/start">Get Started</a>

This little snippet, pulled directly from the Drupal.org homepage, shows how your header can have 10 levels of nesting before getting to a single piece of content. The sad thing is that this is a relatively tame example! I can tell you from experience that it can go much deeper.

This “div soup” might have helped us when our job was to match a static Photoshop comp to a page full of markup, but as our needs matured, we longed for more control.

Static Markup: 0% Automation, 100% Control

If we were working on a small project, or simply had a page where we had a huge body field to fill out, controlling the markup was pretty easy. While this situation offered great flexibility, it also meant that we were responsible for maintaining all of the code. Changes that would be simple in a CMS template had to be propagated throughout the pages by hand.  So we’d write markup like this:

<header>
  <section>
    <nav>
      <div>
        <ul>
          <li>
            <a href="/products">Products</a>
            <ul>
              <li>
                <a href="/socks">Socks</a>

To keep things simple, “semantic” markup was preferred, relying on HTML5 elements and their relative positioning in order to apply styles instead of classes. Without classes on our markup, and having been burned by primary navigation styles bleeding down to secondary navigation anchors, we often ended up with long descendant selector chains like this:

header > section > nav > div > ul > li > a {
  color: white;
}
header > section > nav > div > ul > li > ul > li > a  {
  color: blue;
}

This specificity nightmare ensured that every selector we wrote for our hover and active states was at least this long. You don’t even want to see how tertiary navigation is styled. Let’s move past our past and look at more current practices.

Striking a Balance Between Control and Automation

As the frontend architect, you’ll need to evaluate the processes that produce your markup. How much control will you have over the order of the content, the elements used, and the CSS classes applied to them? How difficult will it be to change these things in the future? Are templates easily accessible, or will you need to task a backend developer with the change? Is your markup even based on a templating system? Can you propagate changes throughout your entire system, or is it a manual process? The answers to these questions might dramatically change the way you approach building your HTML and writing your CSS.

Modular Markup: 100% Automation, 100% Control

Theutopian state that we are all striving for is a situation where every line of HTML on our site is programmatically created, yet we as frontend developers have full control over the templates and processes used to create that markup. Unfortunatly, you won’t often reach this state. Even in the best situations, there is user-generated content that will have little to no automated markup. Regardless of a CMS’s ability to expose HTML templates, sometimes it’s just easier to let the CMS determine the markup for things like your forms or navigation. But even if you stand at 90%, a modular approach to your markup will provide you the flexibility you want with the automation you need.

Modular markup differs from procedural markup in that we no longer cede power over to the CMS to determine what markup should be used to output any given content. This allows us to use the same markup for two different navigation instances, even though the CMS might have used completely different markup. Modular markup also differs from static markup in that, being programmatically rendered, it allows us to apply a system of classes to the markup and not rely on the element tags and position to determine their visual appearance. Let’s look at that navigation example again with a BEM-style modular approach:

<nav class="nav">
  <ul class="nav__container">
    <li class="nav__item">
      <a href="/products" class="nav__link">
        <ul class="nav__container--secondary">
          <li class="nav__item--secondary">
            <a href="/socks" class="nav__link--secondary">

At first glance, this approach seems quite verbose! And while I will not argue that point, I will argue that it is the perfect level of verbosity. With a class on every element, we no longer have to rely on styling tags or using element position to determine visual appearance. Compared to the dynamic markup, this markup is much cleaner, and dare I say more “modular”? This navigation pattern could be used in several places throughout the site, using the exact same markup. Therefore, it isn’t markup that was created by the CMS and then styled, it is markup that was created, styled, and then integrated into the website’s navigation system.

It All Leads to a Design System

So how do we get started with this modular approach? Well, it all starts with changing the way that we create our pages. You see, there is no page. It’s a myth. A website page is a relic of our past. What is a page? Is it the content at a certain URL? Well, what is the guarantee that the content at a given URL remains the same each time you visit? What happens if you are logged in? What happens if the content on that page is filtered by the time of day, your location, or your browsing activity? The sooner we realize that we are no longer building pages, but design systems, the sooner we can start to create some amazing things on the Web.

A design system is the programmatic representation of a website’s visual language. The visual language, created by our designers, is an artifact that expresses how the website visually communicates its message to users. It is a collection of colors, fonts, buttons, image styles, typographical rhythms, and UI patterns used to convey mood, meaning, and intent.

Just as a spoken language can be broken down into nouns, verbs, and adjectives, our job as frontend developers is to deconstruct the visual language into its smallest pieces. By doing this, we can create rules about how to put it back together again. It is by breaking down the visual language that we learn how to create our proverbial sentences, paragraphs, chapters, and novels. The goal of this conversion is to create a scalable and maintainable codebase that faithfully reproduces anything that the language is able to express.

We’ll dive further into creating design systems in future chapters, but it is important that we understand what we are creating, because before we do that, we need to decide how the design system is going to be attached to our markup.

The Many Faces of Modular CSS Methodologies

There are almost as many CSS methodologies today as there are CSS or JavaScript frameworks. But unlike CSS or JavaScript frameworks, which are usually all or nothing and come with a bit of baggage, a CSS methodology is more of a philosophy about the relationship between HTML and CSS than a prebuilt codebase.

It seems that almost every day you hear about a new approach using some new namespace, leveraging data attributes, or even moving all of the CSS into JavaScript. The great thing about all of these methodologies is that they all bring something interesting to the table, and help you to learn something new about how HTML and CSS can be intertwined.

There is no single perfect approach, and you might find that one project fits best with one, and another project works better with another. There is absolutely nothing wrong with just creating your own methodology, or starting with a popular one and then modifying it to your taste. So if you are wondering where to start when deciding on your own approach, it is best to take a look at a few of the more prominent methodologies, and see what does, and what does not, resonate with the project you are about to tackle.

OOCSS Approach

The following snippet shows the Object-Oriented CSS approach to creating an HTML toggle.

<div class="toggle simple">
  <div class="toggle-control open">
    <h1 class="toggle-title">Title 1</h1>
  </div>
  <div class="toggle-details open"> ... </div>
  ...
</div>

The two main principles of OOCSS are to separate structure and skin, and to separate container and content.

Separating structure from skin means to define visual features in a way that they can be reused. The simple toggle element shown in the preceding snippet is small and reusable in many different situations. It can be displayed using various skins that will alter its physical appearance. The current skin of “simple” might have square corners, but the “complex” skin might have rounded corners and a drop shadow.

Separating container from content means to stop using location as a style qualifier. Instead of styling tags inside of some container, create reusable classes like toggle-title that will apply the required text treatment regardless of what element it is used on. This way you can let an H1 look like the default H1 if no class is applied to it.

This approach is very useful when you want to provide your developers with a large set of components that they can mix and match to create their UIs. A great example of this approach is Bootstrap, a system full of small objects adorned with various skins. The goal of Bootstrap is to create a complete system that is capable of creating any UI that a developer might need to put together.

SMACSS Approach

The same toggle component written in Scalable and Modular Architecture for CSS would look like this:

<div class="toggle toggle-simple">
  <div class="toggle-control is-active">
    <h2 class="toggle-title">Title 1</h2>
  </div>

  <div class="toggle-details is-active">
    ...
  </div>
  ...
</dl>

Although it shares many similarities to OOCSS, SMACSS is an approach differentiated by the way it breaks the system of styles into five specific categories:

Base
How markup would look without classes applied to it
Layout
Dividing the pages up into regions
Module
The modular, reusable parts of your design
State
Describes the way that modules or layouts look under given states or contexts
Theme
An optional layer of visual appearance that lets you swap out different themes

In the preceding example, we see a combination of module styles (toggle, toggle-title, toggle-details), submodule (toggle-simple), and state (is-active). There are many similarities between OOCSS and SMACSS in how they create small modular pieces of functionality. They both scope all of their styles to a root-level class, and then apply modifications via a skin (OOCSS) or submodule (SMACSS). The most significant differences between the two (other than the difference in how code is structured in an SMACSS approach) is the use of skins instead of submodules, and the is prefixing the state classes.

BEM Approach

Finally, here is the same toggle component written in Block Element Modifier syntax.

<div class="toggle toggle--simple">
  <div class="toggle__control toggle__control--active">
    <h2 class="toggle__title">Title 1</h2>
  </div>

  <div class="toggle__details toggle__details--active">
    ...
  </div>
  ...
</dl>

BEM is the third methodology we are going to look at, and is on the other side of the spectrum from SMACSS. BEM is simply a class naming convention. Instead of dictating the structure of your CSS, it only suggests that every element is labeled with a class that describes the following:

Block
The name of the parent component
Element
The name of the element inside of the block
Modifier
Any modifier associated with the block or element

BEM uses a very terse convention for creating these class strings, which can become quite long. Elements are added after a double underscore (e.g., toggle__details) and modifiers are added after a double dash (toggle__details--active). This makes it very clear that details is an element and that active is a modifier. The use of a double dash also means that your block name could be news-feed without confusing feed for a modifier.

The advantage of this approach over OOCSS or SMACSS is that every class fully describes what it accomplishes. There are no open or is-active classes. While those classes make sense in their context (in a toggle element), outside of that context we don’t have a clue what open or is-active means. While a BEM approach might seem redundant or overly verbose, when we see a class of toggle__details--active, we know exactly what it means: the details element, inside of the toggle block, is active.

Choosing What Is Right for You

In the end, the only thing that matters is to find a solution that works for you. Don’t choose a convention because it’s popular, or because another team is using it. All three approaches give you extremely similar tools to work with, and will integrate with a design system in very similar ways.

As I will discuss in Chapter 7, at Red Hat we settled on a mix of SMACSS and BEM. So don’t be afraid to experiment, combine ideas, or come up with something completely unique! Just be aware of the prior art, be able to express why your approach will solve the challenges your project faces, and have a team willing to commit to a single, unified approach. If you decide to use OOSMABEM, then more power to you! I look forward to reading about it.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required