Styling using data attributes
The vast majority of design systems and component libraries organize their styles using class selectors. Generally, this is a good choice, but it's not perfect. Methodologies like BEM are used as a way to organize these classes into component and modifier classes. The biggest issue with this way of organizing styles is that it's possible for multiple modifier classes to be applied to a single element and there would be no clear way for an engineer to determine which of the modifier classes would take precedence.
If you stumbled upon some code like this, how would you know what color it would render as?
<div class="Component is-blue is-red">
You would have to render it or go to the source to determine for yourself to determine that it would be blue.
Specificity of modifier classes is not typically something that is considered when maintaining a design system and many times the final CSS is generated automatically which means that a minor rearranging of code could cause the final styles to reverse order and you might wake up having the component render as red instead of blue.
While this problem may seem contrived, this is a problem that occurs due to tech debt in real-world applications. As business requirements change, styling requirements can change, and it's very easy for multiple modifier classes of the same "type" to be mistakenly left in or conditionally added. Over time this can make code harder to read as it's non-deterministic; without a deep understanding of the CSS, an engineer can't determine what color the component would be without actually rendering it.
Attribute selectors to the rescue
Using attributes instead of classes to manage modifiers helps eliminate this problem and makes the code easier to understand. We can change the styles like so:
This then means the HTML we stumble upon would look like so:
<div class="Component" data-color="blue">
While it's still technically possible to have multiple
data-color attributes on a single element, it's not considered valid according to the HTML5 standard and many linting tools can be configured to catch this type of error.
Benefits of this approach include:
Only a single modifier of each type can be declared
Promotes better accessibility as non-data attributes like
aria-*attributes can be used
According to performance benchmarks, class selectors are more performant than attribute selectors, but not by much. I would argue that for the average web application on modern browsers, any performance issues would go unnoticed and be far outweighed by the benefits of having an easier-to-read and easier-to-maintain codebase.