CSS Utility Classes and “Separation of Concerns” (2017)
adamwathan.me
adamwathan.me
If I need to change the product card on my site it is at component/product-card.scss. If I want it to be different on thr homepage I change it at context/home-page.scss and I targetit with .home-page .product-card. Simple stuff, no inheritance issues either.
From my experience, reusable CSS is somewhat of a red herring. Maintainable and easily located CSS is where you get productivity gains. If you don't know what you have because there are hundreds of utility classes then what good is it.
Also, if you are using utility classes why not just inline CSS? "red-border white-background pad-20" is just as bad.
I wrote an article explaining the method here: https://polylab.co/articles/ccm-contexts-and-components-css....
The only bullet you have to bite is occasionally repeating yourself, but as you say, that part of CSS never really worked well anyway.
If using a pre-processor like Stylus or Sass, you can get a lot of composability with mixins and extensions, if that's what you want, and these can even be cleaner and more flexible than utility classes, because they can use parameters and basic logic.
I just made that one up when I started formalizing the method!
> The only bullet you have to bite is occasionally repeating yourself, but as you say, that part of CSS never really worked well anyway.
I find as well that if you write clean, concise CSS that doesn't have to override anything you get away with far less CSS anyway. So repeating yourself isn't so bad. Similarly, in a project of any longevity, especially if clients are involved, chances are pretty high the things you repeated will eventually diverge. So had you made it DRY you would have to de-compose the coupled components and write it again anyway. With the Contexts, you can make most diverging modifications without ever touching the components, and I feel any more levels of inheritance than that leads to CSS that is more complex than it needs to be.
At the risk of being a broken record, this is similar to an approach that I discovered and have talked about on HN before. I think your approach is a form of "separation of concerns" that makes sense because I've found that CSS becomes less tangled and more comprehensible if styles that represent a concept like a .product-card aren't concerned with how they are sized or positioned on a page; that's the job of something like `.home-page .product-card`, or, in my own practice, `#home-page-product-card` if the element is a layout singleton or `.home-page__product-card` if the element is repeated.
By doing this, not only can I look at my markup and understand what everything means(which I can't do easily with utility classes), but I never find myself needing to use things like `!important` which have consequences to their use.
Those of us who’ve used Tailwind in particular might write “just as good, but better”. The utility classes are satisfyingly regular and well documented. They are higher level than inline CSS, and though obviously congruent, I'd reject a claim of direct equivalence.
Tailwind is to inline styles what assembler is to machine code, and that’s a good thing. With inline styles there is no opportunity to use combinatorial selectors, or to specify units, breakpoints, and other stylistic preferences as part of the compiled utilities.
My experience of a semantic or domain-specific approach to class-based styling is a drop in maintainability and a rise in complications, duplication, and special cases. Especially so in larger, componentised applications. Thinking of elements as purely on-page structures (and not representational of anything greater) helps avoid layering violations, promotes generality of style, and creates more opportunities for extension and substitution.
"The reason I call the approach I take to CSS utility-first is because I try to build everything I can out of utilities, and only extract repeating patterns as they emerge."
I've been puzzling over the same "Separation of Concerns vs. Mixing Concerns" dichotomy ever since the rise of Bootstrap. Something about using Bootstrap's classes never felt right to me, but I was never happy with the amount of duplication in my traditional CSS either... I eventually settled on BEM, but that didn't 100% solve the issues either, and it seems to be getting left behind as the ecosystem gets older.
The idea of starting with the Bootstrap-like functional CSS, and then compositing repeated patterns into components, definitely seems to have tremendous upsides... Looking forward to trying this methodology out in a future project.
Unfortunately following an incident with a broken Lisp machine, a liquid lunch, and an unlicensed particle accelerator, I became trapped in a parallel universe where the HTML ERB anointed CSS by mistake during a drunken night out in Oslo.
The fundamental concept of CSS (best revealed by H.W.Lie's thesis IMO[1]) was to create a rich and versatile and non-Turing-complete set of structural selectors in lieu of DSSSL's recursive logic, and to allow styles to overlay one another; two design choices that only by the application of gallons of irony can explain why most web pages are composed of a bunch of nested DIV elements with hashed IDs and overloaded semantic class attributes, and everyone compiles their assets into a static file.
I switched to Tailwind CSS months ago. Adam Wathan is the hero we deserve.
(That's why every CSS question on Stack Overflow has an answer that says "use this HTML: ... and this CSS: ...". It's rarely possible to use some random HTML and style it in an arbitrary way.)
The CSS designers decided to pick a purely declarative stylesheet language, which is a cool idea and has some interesting properties. One of the things you sacrifice with this decision, though, is the ability to structure the HTML as you wish. You've got two languages you need to play with, and you've got to tweak them in concert.
There's an alternate universe where they chose to use JavaScript as the styling language. The style layer runs in its own sandbox completely separate from any other JS on your page, and all it does is accept HTML trees and lay out and style them. You can structure your HTML in any way you want, and then write 3 lines of JS to style it any way you want. You can implement TeX-style word wrapping or media queries as a library. Your designers don't have to talk to your programmers when they change something, because the HTML is generic from the start.
I've worked on a moderately complex codebase that did something like this and it took a long time to get to a place where new feature requests didn't end up introducing additional complexity and the codebase wasn't a big ball of mud.
For all CSS's faults, most developers can still ship code that is understandable.
However it's not true that "can't make a nice structure in HTML, and then style it with CSS." That is one of the design goals of CSS, and it works.
In a former life I did this for many years, with many CMS and front-end systems. In a well-structured Drupal or Wordpress site, for instance, you can link to one additional stylesheet and override any aspect of the design. Some of the CSS might be ugly, and every now and then you might need the HTML tweaked to add a class, but it works.
Check out [http://www.csszengarden.com]. This is an old site, nearly 20 years old, put up by designer Dave Shea precisely to disprove your point :) It sports hundreds of interesting designs with non-trivial layouts, really pushing the boundaries of what was possible back in the day. All of the designs are CSS-only, and hang off the same HTML skeleton. If you view the source, it's pretty simple.
Doing this with JavaScript introduces all sorts of other concerns: accessibility, security, privacy, maintainability, future-proofness, compute necessary to render, render time, etc. jQuery did basically this, right? Selecting markup and content with CSS syntax to munge them, or attach triggers.
I rarely see people write vanilla CSS these days. Most folks abstract it with SCSS or LESS. That removes a lot of the warts. It's still too easy to end up with 5000 lines of CSS that can only be tested by manual inspection :/
And even if I'm wrong, the last decade has proven that your regular above-average developer can't typically do it, and that amounts to the same thing.
I think that's a different issue that has to do with the web not having proper layout tools. Where in order to position a group of elements, you had to div them. Nowadays, we have `align-self` and `display: contents`.
Yes it is.
I personally don't like the end result, where by "separating the concerns" you end up hardcoding your style in the HTML markup. For me the utility classes are just exposing the CSS rules to the HTML markup, which I think is actually the opposite of separation of concerns because I when I think of "separate" I think the most of writing code in two different files.
> The amazing thing about this is that before you know it, you can build entirely new UI components without writing any new CSS.
So, if you want a responsive/mobile version you would have to either serve a different HTML for mobile or also have all the CSS rules for mobile in the inline class names. Before you know, you have a list of 30 cryptic CSS classes written inside the HTML markup for an element.
> How many times have you needed to style some HTML and thought, "this text needs to be a little darker," then reached for the darken() function to tweak some base $text-color?
Well, why not just use $text-color--darker? Define your swatches globally beforehand and do not allow the creation of new colors anyhwere else in the code. I guess his solution is similar, but with using another CSS class instead of a variable.
Also, what if at some point you decide to redesign your site and change the margin of all text? So you would have to replace all "mar-6" with "mar-12", but only where the margin is for text. This looks like a complex update invloving a lot of bug-prone search and replace. If it was written as ".text-margin { margin: 6px; }" then it was just a matter of changing one number and you know it would only affect the text-related margin. Sharing style across components is not always a good thing, it makes changing things much harder. I guess with this approach you should never change the initial values, as the entire design might be affected in weird ways.
I am not saying this is completely bad, I am just saying there's no perfect way of doing things and everyone and every project is different.
All that said, 100% agreed with your closing sentence! :)
Yep. I like utility classes (F.K.A. OOCSS, Atomic CSS, and Functional CSS) and use them all the time. But it's one tool amongst many. It's not reason to give up small namespaced components (e.g., BEM) or using inheritance when you're doing so clearly and deliberately (Zen Garden Method).
This debate, which is something like 25 years old at this point, always seems to assume a need for absolute purity. But one of the best things about CSS is, depending on what you're trying to build, these techniques can work really well together.
These types of examples basically never match real-world experience, it is never as easy as it sounds. Are you telling me the giant text in headers and small text inside buttons and condensed text in tables and indented text in lists and plain body text all share the same margin size? What happens with text adjacent to an image that itself has a margin applied to it? Or text next to an icon (which technically may be an icon font text glyph).
>> all the CSS rules for mobile in the inline class names.
most of the time, this works great. desktop and mobile can share, for example, font family, color, weight, background color, and use different font-size and padding, to account for the smaller dimensions on mobile. As a developer, having multiple styles inline are a constant reminder to be responsive-minded before making any style changes that might look fine on a dev machine with a huge screen.
you're correct there is no perfect way and projects differ -- but Tailwind's approach is often superior to alternatives
You cannot do this in inline styles:
<div class="md-display--none xl-display--block">…</div>
It's a false premise to say this is "inline CSS". Inline styles are one subset of the entire CSS world and functional classes can hold dozens of advantadges inline CSS doesn't have.
One area that wasn't covered was the ease of creating truly responsive layouts, inline, by defining different utility classes to the same elements based on screen size. "Responsive" is so much more than just the placement -- quite often, mobile version needs different font sizes, overflow behavior, thinner margins (since there is less screen space), etc. Very easy to do with the utility approach.
Almost nothing in css is identical, but almost everything is similar.
This becomes especially true as you move up the complexity spectrum. Primitive things can be identical (fonts, colors, input boxes, etc.), but the real higher-order actual components like pages, forms, sections rarely are. In fact, the little differences, tweaks and custom-tailoring that are manually applied based on context are what separate professional design from robotic enterprise-style Frankenstein design.
I think the answer is that you need both semantic css and utility css. Utility css is used to define the rigid primitives that make up the general design language, while semantic css provides the hooks and separation needed to be able to uniquely compose those primitives into custom higher-order components.
So to answer the author's question regarding how to model "author-bio" and "article-preview", I would personally keep their separate semantic titles, but style them using common utility primitives classes and custom css within the css.
I use these a bit when I’m being more thoughtful, it’s a nice way to think about creating chunks of style that don’t get included in the output, bit surprised they weren’t mentioned yet.
Personally I think visual consistency is important and you shouldn't make things look different in different places, but it's not always up to me.
With utility classes, your code has no way of knowing that "bg-red" should override "bg-blue". (And frequently it won't, if "bg-blue" happens to come later in the CSS file.) So people end up inner-plaforming their own clunky solutions on top.
See my (totally unscientific, yet scary) poll of my audience, which heavily leans towards frontend with a focus on React: https://mobile.twitter.com/mxstbr/status/1038073603311448064...
Only 43% got it correct!
.body--homepage .searchbox { color: red }
but, you use .searchbox on other section and you want it green
.body--article .searchbox { color: yellow }
To me this sounds an akward usecase but you can do this with functional classes no problem: just write the class to the point you want to change on whatever place you are instead of relegating these conditionals to the CSS.
If the markup changes, the CSS changes. The nested SCSS should closely mirror the DOM.
I can only imagine utility classes will simply create many problems with naming down the road. I would hate to have to deal with all that.
Here’s a link to the responsive design guide in the Tailwind CSS docs that covers it in detail:
https://tailwindcss.com/docs/responsive-design
The TL;DR is you have breakpoint-specific versions of your utilities, so you can define all of your responsive behaviour directly in your markup.
For example this element would be `block` by default (on mobile) and `flex` when the browser is at or above some defined “medium” breakpoint:
<div class=“block md:flex”>
Two years after writing that article I can say CSS definitely feels “solved” to me, and every time I have to go back to an old Tailwind project it’s very easy to make changes.
If you’re building things with React, I’d also encourage you to explore projects like Emotion and Theme UI which allow you to work with the same philosophical approach (styling elements directly instead of through “hooks” and some separate style layer) but with a bunch of neat advantages that aren’t possible with a regular class/CSS-based API, like having better control over how style definitions override each other when applied to the same element.
Personally I’m still very happy using Tailwind even in React projects, because the syntax is more terse and it’s nice to have a singular styling paradigm that I can use even outside React projects.
"Codeless tracking" systems let you put in the CSS selector for a button and find out how many people clicked on it. UI test automation tools let you specify the CSS selector of an element to click on. User stylesheets let people make tweaks to your site if they have accessibility, usability, or aesthetic concerns.
If all your elements have class names like "mt-2 bg-black font-semibold text-white pt-2 pb-3 flex justify-left", you're making it a lot harder for you, your users, and your co-workers to take advantage of this ecosystem. At the very least, you might want to consider also putting semantic classes on your elements.
I'm glad that backbone and jQuery apps are in my rear view mirror at this point.
>Before, we could just open up our stylesheet and choose new styles for either of the two components. Now we'd need to edit the HTML! Blasphemy!
The reason you can't is because the markup declares two things to be equivalent, obviously then CSS can't tell them apart. You're not mixing concerns you're lamenting the fact that CSS can't be concerned with markup.
Throwing separation of concerns overboard and making HTML concerned with styling doesn't seem the best resolution.
It is more common these days than it was for websites to support multiple styles/layouts. Whether that's because they're responsive or because they allow you to enable dark mode or because they support rtl layouts. Class names such as text-dark-soft or align-right will be misleading if text-dark-soft ends up closer to white and align-right is left aligned as a website is evolved to support these features.
Not a criticism of this approach; this is easily fixed by using names such as text-emphasized-soft or align-start (for example), just pointing out the examples in this article could be lead to problems
CSS variables can also be used to encourage picking colors or padding/margin from a curated list. The approach in this article is not required to solve that problem
Would you mantain .display--block? how? .color--red?
Product-card is not an utility, that's why it's a problem to maintain such classes.
Then my workflow for that case would be: create the new color value, compile the CSS, change the class name in those places where I want to use the new red.
It can look sloppy, but I find that it's important to provide as many "hooks" as possible to elements; especially dynamically-generated elements.
This is because I've written tools that were meant to be integrated into sites, as opposed to the end site, itself.
It was important that the user of the tool be able to exert as much control as possible over the rendering.
It does look messy as hell, though. Inspect Element is very helpful. Since a lot of the dynamic code is optimized anyway, or rendered by AJAX, display page source is kinda worthless.
It's all about keeping the specificity as weak as possible, while allowing the CSS to focus on individual elements, or collections of elements.
It's dated, but still absolutely relevant.
Here's the start of the specificity section, which goes on for some time: https://littlegreenviper.com/miscellany/stylist/introduction...
In CSS specific rules overrides general rules. So .article.bio would inherit all .article rules and also override.
I like to start out my style sheet (CSS) by only using the semantic HTML elements like h1, button, etc. Then when the design advances and I go down to the small details - I add more and more specific rules.
---
> Utilities force you to choose:
Is this text-sm or text-xs?
Should I use py-3 or py-4?
Do I want text-dark-soft or text-dark-faint?
You can't just pick any value want; you have to choose from a curated list.
Instead of 380 text colors, you end up with 10 or 12.
For a while now I've been effectively writing semantic css (nested or more recently bem) composed out of non semantic sass helper functions. The benefits of this are being able to think about namespacing in very simple terms. It can make for fairly large css output files though, something BEM helps with a bit.
1. Start with a given approach, but say you feel something "off" because it doesn't quite fit "best practice xyz".
2. Use another approach, which also doesn't fit "best practice xyz".
3. Argue that the best practice shouldn't be a best practice.
This of course, should mean that the very first approach is just as alright as the second, yet here it is used to defend only the second (sure, there's a token acknowledgement of the first approach being valid too under the new assumption, but everything afterwards is a long "but not really, second one is the true better one").
Back to the topic at hand, the article depicts a circle: let's move away from inline css by... moving it one attribute over, to "class". The token attempt at contradicting this is unsuccessful one because, as much as there's no limit in the inline css you can do, there's no limit to the amount of classes you can add. So it all ends up feeling a bit self-defeating.
Ultimately, this article, IMO, is a good example of this: https://christine.website/blog/experimental-rilkef-2018-11-3....
Then again, I'm a developer first and foremost, maybe I'd like this better were I a designer.
Architecture is not a striving toward perfection, it's a collaborative process of finding heuristics that give good results based on real-world constraints.
Things to consider:
Utility classes do not do well when there are multiple states involved (as a simple case, consider :hover). Libraries that use the CSS utility classes do not follow the "Open-closed principle" or the "Dependency Inversion Principle" – depending on the mental model you use.
The term "functional CSS" is a misnomer, since the composition of styles happens at the HTML layer. Calling it "mashup CSS" would be far more accurate.
One huge problem in writing any software is figuring out how to make good decisions about where and how to compose smaller building blocks into bigger ones. Category Theory helps with many of these questions, but is far from sufficient to answer all of our questions. So it's quite valuable to discuss and try new things.
For content heavy sites like ecommerce/marketing/blogs, I use pretty much utility classes exclusively. There usually isn’t much reuse of UI components on these types of sites so maintenance is much simpler compared to semantic classes. With semantic classes you’ll have a lot of one off styles.
I actually really enjoy writing sites like these since I can code a site start to finish without touching any CSS files. Using utility classes I probably finish 25%-50% faster than if I were to use semantic classes.
On more web app type things like admin panels, I use a mix of mostly semantic classes and a few utility classes. I only use utilities to make sure there aren’t a million different padding/margin/font size values.
In the earlier days of React I tried using all utility classes and it just felt like there was too much going on in the JSX. If you use utility classes, you know the class attribute can get pretty long - sometimes needing to be broken into 2 lines. The CSS tooling with Webpack has come a long way and solves a lot of the problems that all the utility class frameworks were trying to solve around 2015.
The answer to anyone asking "how is this not inline styles", is in the article. With utility CSS, you don't use 100% of everything that CSS can do, and just pick the exact snippets you need for your theme.
A link can refer to a user profile page or a news article. Both use the generic anchor "component", which comes from the wider universe of components applicable to most/all HTML docs. HTML: `<A class="user">`, CSS selector: `A.user`.
To extend this, a media card could represent a video or a book or a sidebar item. Both use the media card component's HTML structure but can be tweaked visually with CSS. The root element of the video media card would be `<DIV class="MediaCard video">` and would be identified with the selector `DIV.MediaCard.video`.
(PascalCase is arbitrary but I like that it harks to class naming conventions from OOP languages. You'd need some other naming conventions as well. I like `_title` for internal element class names, like private class members from OOP languages. Internal selectors would be built using the performant child combinator. Some of these patterns are more verbose than current alternatives, but it's also the minimum necessary to get crystal clarity around inherited and cascaded values. Preprocessor nesting makes this less painful. And `-highlight` for variants, because they resemble CLI switches. What about non-generic components? Those aren't "components" as defined here - they are simply higher-level HTML fragments, typically organized in source as an HTML template.)
This gives you two dimensions along which to flexibly organize things - the generic forms and the domain-specific applications, and should keep you from slipping down the slope to utility-dominant CSS.
.my-class {
.utility-class-1;
.utility-class-2;
}
Reusable constants also help a lot.TypeScript/JavaScript:
export default Vue.extend({
data() {
return {
isActive: false,
};
},
computed: {
componentClasses() {
return ["margin-4", "padding-3", {
"color-red": this.isActive,
"font-size-30": this.isActive,
"font-weight-700": this.isActive,
}];
}
}
}); .element.active {
color: red;
font-size: 30px;
font-weight: 700;
}https://vuejs.org/v2/guide/class-and-style.html#Binding-HTML...
https://vuejs.org/v2/guide/class-and-style.html#Array-Syntax
In your template:
a) <div :class={active: isActive} />
b) <div :class="{isActive? 'active' : 'inactive' }"/>
I think "Every-Layout" (https://every-layout.dev) makes an extremely compelling case for this approach. I'd love to see an example of a design system leveraging these beautiful, coherent, logically sound typography-based layouts, incorporated w/ modern component architecture, ready to be customized and themed. IMHO that'd be as transformative now as twitter bootstrap was when it first arrived.
From my experience, reusable CSS is somewhat of a red herring. Maintainable and easily locates CSS is where you get productivity gains. If you don't know what you habe because there are hundreds of utility classes then what good is it. Also, if you are using utility classes why not just inline CSS? "red-border" is jusr as
A-b_c is so multiplicative
Components have been a solved problem in desktop apps for decades
You have layout and styles applied to components positioned in the layout
What's really problematic is having a presentation layer that's split in layout and styles in the same context (CSS) while content and structure are both exposed in HTML
If we had layout in HTML and styling in CSS it would be easier to reason about it
HTML was born to give the document structure, layout is in the same league if you ask me, but then styles became the only way to lay out content, HTML lost its purpose of being the way to structure content in a meaningful way so you have the same HTML behaving diffentely with different layouts just because CSS can work on both of them
If footer meant "stuck at the foot of content no matter what the content is" we could have saved a lot of engineering hacks around making it reusable
If a website is small then I write plain HTML/CSS/JS in the old school way and all of these abstractions/systems are YAGNI.
If a website is large enough for these abstractions to matter then I'm using React with inline CSS. I get reusability and composition without a noticeable performance tradeoff. You can still build your components to decouple style from content where needed. Up to you.
Years later, no issues.
If you're doing this in React, you should probably be reusing a component.