Using data attributes to write CSS components
In recent years, CSS architecture has become increasingly important when authoring websites. Web developers no longer simply add selectors until the result looks right. Instead, we consider the architecture of our CSS files and how it helps us work more efficiently. After the recent redesign of my website, I would like to share some thoughts on how I write my CSS components.
Basic principles
The focus of this whole debate lies on creating reusable modules that are decoupled from their content. This way stylesheets become much DRYer and writing HTML documents becomes a lot easier as you can rely on your modules to do much of the heavy lifting.
This basic principle remains the same throughout all methodologies. However, the execution of those ideas differs. Sometimes components are grouped and prefixed (as in smacss), sometimes the focus lies on making the structure clearly visible (as in BEM) and sometimes the architecture defines a mindset, without providing much guidance on writing the CSS (like OOCSS).
The problem with those approaches
While all these architecture styles have good ideas and do a good job of guiding CSS authors in the right direction, they all have their downsides. Consider, for example, a button with some different styles, written using the BEM style.
<a class="button button--primary button--wide">Button</a>
.button {
// ...
}
.button--primary {
// ...
}
.button--wide {
// ...
}
The problem is instantly visible. We end up with three classes. One for the basic button (button) and two for its modifiers (button--primary button--wide). Using a preprocessor you could extend the base class from within the modifiers, but this is also not a very elegant solution. To solve this exact problem, Glen Maddern and Ben Schwarz came up with AMCSS.
Attribute Modules
The idea is to use custom attributes on your HTML elements, instead of using classes. On their website, they provide the following example.
<!-- Large primary button -->
<a class="btn btn-primary btn-lg">Large primary button</a>
<!-- becomes -->
<a am-Button="primary large">Large primary button</a>
This way, you don’t have to use a base class as the attribute itself acts as the base. It’s also more readable and, in my opinion, easier to write. In your CSS you would use attribute selectors, like this:
[data-button] {
// ...
}
[data-button~="primary"] {
// ...
}
[data-button~="large"] {
// ...
}
This example uses the tilde-attribute selector. This matches a keyword in a space-separated list. You could also use other attribute selectors, but this way you have a strict format to follow, preventing you from making mistakes.
As you can see, AMCSS doesn’t let you write any less CSS. Instead, the CSS is more efficient and you can see clear benefits in your HTML.
I follow this style most of the time. However, I’m using standard data-* attributes, instead of custom attributes like am-button. This allows me to add and remove modifiers through JavaScript more easily.
link.dataset.button = 'primary large';
An example: grid columns
To give you an example of how I use this style I’d like to show you my grid columns. The grid itself is a data-grid component, while the columns are data-col components. Just including those attributes in an HTML tag is enough, they don’t need to have a value.
<div data-grid>
<div data-col="1-2 M1-4 L1-5"></div>
<div data-col="1-2 M1-4 L1-5"></div>
<!-- ... -->
</div>
As you can see I’m using a standard grid component (possible modifiers would be gutterless or rev) wrapping some column components. My setup allows me to define columns for different breakpoints using a custom prefix. No prefix applies to all screens (no media query), ‘M’ applies to medium screens and ‘L’ applies to large screens.
[data-col~="1-2"] {
width: 50%;
}
// ...
@media (min-width: 40em) {
[data-col~="M1-4"] {
width: 25%;
}
// ...
}
@media (min-width: 60em) {
[data-col~="L1-5"] {
width: 20%;
}
// ...
}
I’ve created a Sass mixin to set up the column components in order to make the whole setup even more flexible.
Conclusion
If, like me, you have never felt entirely comfortable with BEM or similar concepts, AMCSS may be a good alternative. It addresses many of the problems other approaches face and makes writing HTML considerably easier.