The Role of Utility Classes in Scalable CSS
Lately, I’ve been thinking about how to write scalable CSS. Not so much about how to pull off snazzy effects, or take advantage of the latest modules, or play clever tricks with preprocessors — but how to create stylesheets that will remain coherent, maintainable, consistent, and extendable as projects grow and change — and how to do this systematically.
I’ve been reading everything I can find on the subject — which includes some very good stuff. (I’ve put together, and plan to maintain, a Scalable CSS Reading List to share what I consider the most important resources.) But there’s one particular question that keeps pestering me, a question that is rarely addressed, and never at length: Where exactly do “utility classes” fit in? — why, when, and how should they be used?
Every frontend framework includes some utility classes, but none offer substantial advice about when or why to use them. Considering that any given CSS declaration could be transformed into a utility class, is it possible that every declaration should be? If you can build a component, a page, or a whole site with utility classes alone, should you? (In “Challenging CSS Best Practices”, Thierry Koblentz argues for such an approach: “Atomic CSS,” he calls it.) If not, where do we draw the line; how do we determine if we’ve gone too far? Can reliance on utility classes hurt maintainability? The mind swims.
Two CSS Paradigms: Component and Utility Classes
All the scalable CSS approaches I’ve learned about endorse two distinct paradigms of classes: I’ll call them component and utility classes.
These two paradigms represent fundamentally different methods of styling; taken to extremes, they can result in dramatically different code and dramatically different workflows.
(I consider the older dichotomy between “semantic” and “presentational” classes outdated: I fully agree with Nicolas Gallagher that “the primary purpose of a class name is to be a hook for CSS and JavaScript” and “class names should communicate useful information to developers.” Either component or utility classes could be considered “presentational,” by the old logic, and I wouldn’t consider that to be a problem.)
Component Classes
A component class encapsulates the declarations required to style an element within the context of a component.
The HTML simply references that component’s classes, and maybe some modifiers, to implement the pattern. Bootstrap’s .btn
exemplifies the single-element component. Other, multi-element Bootstrap components rely on namespaced classes or particular HTML structures. Systematic naming conventions like BEM’s and SUIT’s help to conceptualize, implement, and scale the component paradigm.
Using component classes (and SUIT naming), some navigation markup might look like this:
The class .Nav-link
will apply all the rules necessary to style that <a>
in that context. The .Nav
component as a whole is reusable, but the .Nav-link
class will never be used outside of its context — outside of a .Nav
.
Keep in mind that what I’m calling “components” can be as granular as the situation demands, and “components” can contain other “components.” They are simply coherent, reusable patterns, and might address any level of a design — atoms, molecules, organisms, templates, or pages, in the terminology of Atomic Design. Also (with a nod to SMACSS), components might be organized into categories targeting different responsibilities — for instance, typography (like a .Heading
) distinguished from layout (like a .Grid
) and interface (like a .Button
or a .Nav
). OOCSS fans might call them “objects.” The point is that component classes represent named units, explicitly related to a reusable pattern, whose styles are defined and modified in a stylesheet.
Utility Classes
A utility class applies a single rule or a very simple, universal pattern. Think .float-right
, .align-center
, or .font-small
for single rules; .list-unstyled
, .clearfix
, and .visually-hidden
for patterns. (Sometimes they’re called “helpers,” as in Bootstrap.)
Utility classes are as simple, composable, and reusable as possible; so they will (and are intended to) end up scattered throughout your markup, performing their little role in diverse and sundry contexts. They must be strictly categorized and tightly focused, or they lose their … utility — their raison d’être. They must epitomize the single responsibility principle. Unlike a component class like .Nav-link
, a utility class like .float-left
could show up anywhere, on any element, in any context, so we need to know that it will always do exactly the same thing. That’s why The Authorities agree that utility classes must never change, and why they are a rare case when using !important
might be the right choice).
(If you’re at all uncertain what I mean by “utility class,” have a look at some collections: Foundation’s, Bootstrap’s, UIKit’s, GroundworkCSS’s, and SUIT’s.)
More so than component classes, utility classes bring styling directly into the markup, without even the mediation of a name. Design patterns can be anonymous aggregates of globally shared styles. So if you want to change the appearance of an element styled with utility classes, you’ll have to change the markup — the class list, specifically — not the stylesheet.
Typically, it’s suggested or assumed that God made utility classes for straightforward situations calling for very few rules. You just want a button floated right, a centered heading, a different color, invisible text. Why give an element its own name, its own selector, its own ruleset in some stylesheet — why conceptualize a “component” for it — when all you want is to apply a very common, probably very basic pattern?
Theoretically, though, entire components and entire sites can be constructed by piecing together utility classes. Relying on utility classes alone, the navigation markup from above might look like this:
More Comparing and Contrasting
- With component classes, implementation of a new design pattern will probably involve the creation of new classes and more lines of CSS. With utility classes, new design patterns can often be implemented with existing classes, no new CSS.
- With component classes, maintenance and modification of styles happens in the stylesheets. With utility classes, it happens in the HTML, in class attributes.
- Component classes entail repetition in stylesheets. Utility classes entail repetition in HTML.
- Component classes are scoped to their component and should be built to prevent leakage; so one component can change without affecting others. Utility classes are global dependencies; so any change to a utility class will affect any number of elements.
- Component class names primarily represent the relationships of component parts, usually without reference to style. Utility class names exclusively and transparently represent the styling applied.
- Component classes are flexible: they can be attached to arbitrary, variable styles, different themes, etc. Utility classes are rigid and unchangeable — ideally, they are just as reliable as a declaration in CSS.
Some Alleged Benefits of Utility Classes
The component-class paradigm is a more straightforward, traditional utilization of CSS, I think. Apply a distinct class and tie that class to some styles. Add, modify, and remove declarations in the stylesheet. And so on. Though you may never achieve the fabled “Separation of Concerns” — and may not even care to — you will be closer to it and reap some of its rewards if you use component classes more, utility classes less.
So it’s worth articulating and assessing what people consider to be the benefits of utility classes. Why do we use them at all? Why are they included in every frontend framework? And why might some people consider using them exclusively?
1. DRYness and Consistency
Apply .float-left
whenever you need something floated left, and you won’t end up filling stylesheets with repeated float: left;
declarations. (CSSLint will be proud of you.) Reuse color utilities (e.g. .bg-dark
), and you won’t have to copy and paste hex values; additionally, when the design changes, you can alter the value in one place instead of many. Rather than throwing 3px
, 6px
, and 7px
of padding here and there — whatever seems right at the time — rely on a simple .pad-sm
for consistent spacing. Utility classes can help you reduce repetition in your CSS and inadvertant irregularity in your style.
This benefit, however, is negligible if you use a preprocessor. The reuse of single-rule classes like .float-left
merely shifts repetition from CSS to HTML. It’s still repetition: you still tell every individual element you want floated left to float left, in one way or another. And for more complex patterns and particular values, a preprocessor — with variables, mixins, and extends — enables the same degree of consistency by other, arguably more author-friendly, means.
2. Filesize
By taking repetition out of the CSS, utility classes reduce CSS filesize.
Preprocessors can’t match this benefit. Using mixins and variables to repeat patterns and values does result in DRYness and consistency but does not cut back on the size of the delivered CSS: every repeated mixin and variable generates repeated declarations.
Sass @extend
s can be used to reduce some repetition — but they are problematic in other, key ways. And rather than advocating @extend
s, I’d argue that this benefit is questionable. CSS filesize should be one of your last optimization concerns; and once Gzip gets involved, it’s possible that the savings won’t make a substantial difference. In any situation where the scalability of your CSS is at issue, where there’s any danger of things getting messy and confusing and out-of-control, authoring and maintenance considerations should trump kilobyte savings.
3. Expedience
There is no question that utility classes are handy, allowing HTML authors to apply styles more directly. Especially for those elements that require only one or two rules and don’t play an obvious role in some component structure — why add an extra layer of abstraction?
If you move quickly from project to project, a full set of utility classes — or a method of generating them as needed — could speed up your work. And it’s possible that utility classes could help backend engineers, or other collaborators, get things done without modifying any stylesheets.
There’s no denying this benefit; however, it needs to be considered in the context of your workflow, because often expedience is much less important than long-term maintainability — though sometimes it is the top priority.
Some Dangers of Utility Classes
Each of the dangers below would be minor, even trivial, if you use utility classes sparingly. But the more you rely on utility classes, the more significant these dangers become.
1. Inconstant Appearances
When an element’s appearance won’t stay constant, reliance on utility classes can become troublesome. Responsive design is one such circumstance: For example, if things float right at some widths but not others, different utility classes will have to be made to target different widths — something like float-right-sm
, float-right-md
, and float-right-lg
. Or else you might need to develop a more complicated system that uses JavaScript to enact style changes (Thierry Koblentz does something like that).
You know things are going downhill if you ever end up with elements bearing the utility class .float-right
that are not, in fact, floating right, for one reason or another.
Similar issues will arise if your project involves themed variations that rely on the same markup. In such cases the abstraction of component classes can allow for flexibility that the directness of utility classes precludes.
2. Readability and Reusability of Component-Relationships
The more you rely on utility classes, the fewer of your design elements will be explicitly associated with components, with class names that reflect their relationships to one another (like .Nav
, .Nav-item
, .Nav-link
). Although the styles will be transparently communicated by the utility class names, the conceptual structure of the components and the page will not; and that ambiguity might result in some confusion down the line, when somebody is trying to read and understand your code, maybe trying to reuse a particular pattern.
3. Encouragement of One-Off Styles
Without component classes, any repeat occurrence of a pattern might feel more like a re-implementation than reuse — which could lead to slight tweaks here and there, variations that seem right in the heat of the moment but add up to inconsistency. At some point you may realize that your broad set of utility classes gave HTML authors too much freedom to diverge, to favor expedience over architecture, and the maintainability of your code suffered.
4. Too Many Decisions
With utility classes, it’s up to HTML authors to apply the right cluster of styles to the right elements. A good component class system, on the other hand, might allow authors to create HTML without knowing or caring much about why it ends up looking the way it ends up looking.
An important goal of any systematic methodology is to reduce on-the-spot decision-making — and component classes do that better than utility classes.
Conclusions, Prescriptions, Guesses
Light use of limited utility classes, for patterns that you know will never change, should not cause any problems that can’t be mended easily — by removing the utility class as needed, shifting the style-burden back into the stylesheets.
And there are some particular circumstances when heavy reliance on utility classes — building complete components or pages solely with them — might be ok:
- If you are not using a preprocessor to facilitate DRYness and consistency.
- If your workflow makes it just as easy or easier to adjust styles by modifying class lists in templates than by modifying rule sets in stylesheets.
- If your responsive design hinges on very reliable breakpoints, and that’s all the inconstancy you anticipate.
- If you are building a unique pattern, something you don’t plan on rebuilding the exact same way in other places.
- If filesize is at an absolute premium.
In most situations, though — especially for large-scale projects and reusable components — it seems to me that the benefits of systematic component classes will prove more valuable than the expedience of utility classes. By using component classes, you get the communicative value of naming element groups and relationships; you encourage the creation of coherent, relatively self-contained components intended for reuse; and you allow for the flexibility to vary styling, to some degree, without changing markup.
I hope that’s enough words for you. That’s enough for me.