top | item 21553496

CSS Utility Classes and “Separation of Concerns” (2017)

280 points| aarondf | 6 years ago |adamwathan.me | reply

106 comments

order
[+] ehnto|6 years ago|reply
I use a method called Context and Components. The idea being to build components, and to modify those components if they need to change base on context.

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....

[+] karaterobot|6 years ago|reply
I didn't know there was a name for this method, but it's what I've settled on as well: Namespace the classes by their component's class name, and then override them, if necessary, in another namespaced context. It's pretty simple, as safe as can be expected, and works well for sites of all sizes.

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.

[+] inopinatus|6 years ago|reply
> Also, if you are using utility classes why not just inline CSS? "red-border white-background pad-20" is just as bad.

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.

[+] wizzzzzy|6 years ago|reply
I've used a similar approach in the past but found that having styles for each component also defined in vaious 'contexts' became quite confusing. Using modifier classes defined alongside the component I found to be easier to understand / follow and also improved usability of those those styles across the codebase.
[+] ravenstine|6 years ago|reply
I really like this.

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.

[+] Andrex|6 years ago|reply
The money quote for me:

"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.

[+] inopinatus|6 years ago|reply
Two decades ago I was overjoyed to discover that Scheme was finally going to have a useful application beyond illustrating SICP and writing koans to amuse myself, because DSSSL was on the cusp of evolving into the last document styling language anyone would ever need.

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.

[1] https://www.wiumlie.no/2006/phd/css.pdf

[+] ken|6 years ago|reply
The fundamental problem I see is that CSS is far too weak for proper separation of concerns. You can't make a nice structure in HTML, and then style it with CSS. You have to structure your HTML from the start in a way that it's feasible to style it with CSS.

(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.

[+] sonofhans|6 years ago|reply
I agree with you that CSS is weak, especially from today's POV, and that HTML & CSS can be unwieldy together. Also that "It's rarely possible to use some random HTML and style it in an arbitrary way."

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 :/

[+] underwater|6 years ago|reply
Writing code to transform arbitrary XML/HTML trees to a layout is really hard. Naive solutions tend to break down as soon as you get weirdly structured trees. Expressing "Handle <span> nodes like this unless there is a child with class 'foo', or we are descended from a <p> with class 'bar'" in imperative code is hard.

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.

[+] aliveupstairs|6 years ago|reply
> (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.)

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`.

[+] XCSme|6 years ago|reply
> Isn't this just inline styles?

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.

[+] listenallyall|6 years ago|reply
>> If it was written as ".text-margin { margin: 6px; }" then it was just a matter of changing one number

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

[+] meerita|6 years ago|reply
No it's not.

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.

[+] chrisweekly|6 years ago|reply
The subatomic class names aren't cryptic if you spend any time at all using them. But more objectively, you skipped the critically important composition phase! That's where you take those class names and group them in ways that make sense for your component hierarchy. Tailwind supports this directly via `@apply`. (Other equivalent mechanisms for composition exist.) If you're hand-writing raw HTML and manipulating the DOM directly, you're really missing out. Component-oriented architecture (exemplified by React) is powerful, and implies a different "shape" for the boundaries implied by the phrase "separation of concerns". In a world where everything can be encapsulated in components -- including styling and error boundaries -- hanging on to certain rules of thumb or best practices applicable to older paradigms can be limiting and problematic. "The difficulty lies not in the new ideas, but in letting go of the old."

All that said, 100% agreed with your closing sentence! :)

[+] coffeefirst|6 years ago|reply
> There's no perfect way of doing things and everyone and every project is different.

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.

[+] kristiandupont|6 years ago|reply
In addition to meeritas point about pseudo classes and media queries, I'd add that the fact that it narrows down the space is an important factor. The classes mean that I need to find the best fit rather than spend hours matching pixels by hand. It's like going from drawing charts on blank paper to grid paper. You stay within certain boundaries. It seems like a detail but I find that in practice (and in fairness, I've only been using Tailwind a bit so far), this makes a significant difference to the way I think and work with UI.
[+] listenallyall|6 years ago|reply
(Article is from 2017). TailwindCSS is a terrific library.

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.

[+] james_s_tayler|6 years ago|reply
I find Bulma strikes the right balance in this regard. It feels like bootstrap, only much cleaner, but then it also has these nice responsive classes you can apply. It makes it very, very easy work with.
[+] thinkloop|6 years ago|reply
The real issue with css is that there is an enormous amount of code re-use while at the same time almost no code-reuse at all. The author's example highlights this perfectly: he has a "media-card" representing both the "author-bio" and "article-preview" but the "author-bio", in this case, needs to be slightly different. This, to me, is the quintessential css problem.

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.

[+] kian|6 years ago|reply
In programming, this problem would be solved with something like higher order functions or parametric data types, allowing us to both abstract out the commonalities and maintain incredibly specific, easily modifiable, highly customized functionality. Is there an analog in the CSS world?
[+] beizhia|6 years ago|reply
From my experience, using utility classes like this just means that you'll have a lot of messy overriding to do when your reusable components need to look different in different places.

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.

[+] csande17|6 years ago|reply
At least React's "pass an object to the `style` prop" makes it relatively easy to do that overriding.

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.

[+] meerita|6 years ago|reply
Let me see this:

.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.

[+] whytaka|6 years ago|reply
As a designer, the only thing that's important for me in managing CSS is isolation of styles. I apply a reset at the top to make everything as minimal as possible and then apply styling to each element and the naming convention would be such that it applies pretty much only to that element (or only that and its children if I'm being lazy).

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.

[+] IggleSniggle|6 years ago|reply
I can definitely see the advantages! But don't you find yourself writing the exact same CSS hundreds or even thousands of times with this approach?
[+] LaMarseillaise|6 years ago|reply
I think one of the major advantages of Web Components is that styles can be isolated without the need for name-spacing. There is no need to carefully structure CSS according to the DOM, because this isolation is included with the shadow DOM. Incorporating CSS grid takes this even further, allowing these isolated designs to be responsive to the layout without needing to know what it is.
[+] adamwathan|6 years ago|reply
Author here — a common question I’ve seen in the comments that I didn’t cover in the article is how you do responsive design with this approach.

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.

[+] 38932ur98u|6 years ago|reply
Thanks for the fantastic library! I've really enjoyed using it and it has sped up dev time phenomenally. However, I am curious: what is it like using tailwind on huge projects? I can imagine with many developers working on a codebase the "extract repeated patterns to their own new class" philosophy can get easily ignored, or difficult to track where some series of classes is being repeated.
[+] cdirkx|6 years ago|reply
I think utility classes _first_, and not utility classes _only_, is a reasonable approach. My purist mind would prefer something like semantic classes as a sort of 'public', 'HTML-facing' API, implemented using 'private' utility mixins, keeping the separation of concerns. But in practice, I know I have wasted a lot of time thinking about class hierarcies and separating components, especially when the design is nowhere finalized and I just want a 4px padding.
[+] csande17|6 years ago|reply
I think the utility-based approach overlooks an important point: semantic class names are useful for a lot of things besides writing your styles.

"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.

[+] 1_player|6 years ago|reply
Nothing stops you from adding a semantic class name or ID to a tag for easy reference.
[+] bouchardm|6 years ago|reply
I never liked to do CSS, but I must admit with the library this guy released (tailwindcss), it's really fun :)
[+] contravariant|6 years ago|reply
>What if we wanted to change how the author bio looked without changing how the article preview looks?

>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.

[+] martinbooth|6 years ago|reply
There are lots of positive comments in this thread in support of this approach and I think it's great when anyone challenges the status quo.

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

[+] chrisweekly|6 years ago|reply
MODS: OP is from August, 2017; maybe add "(2017)" to the title given how long 27 months is in webdev?
[+] jameslk|6 years ago|reply
Maintenance of these utility class CSS codebases is such a pain. I've had the pleasure of dealing with it. What if you want to tweak one of your utility classes ever so slightly? If your codebase is big enough, you've just created enormous amounts of potential regressions.
[+] andrethegiant|6 years ago|reply
This is why CSS-in-JS solutions outshine utility classes IMO – if you deal with stylesheets directly, it's hard to avoid treating CSS as append-only files that grow linearly with the size of your codebase. With CSS-in-JS, on the other hand, the styles get generated for you, at a size that grows logarithmically to the size of your codebase (style rules of the same value get pulled into their own deduped classes).
[+] meerita|6 years ago|reply
Give me an example of that.

Would you mantain .display--block? how? .color--red?

[+] rhythnic|6 years ago|reply
The biggest win when using functional css, or css utility classes, is that it is extracted out into a library already, in which case there is nothing to create or maintain (just the downside of messy classnames).

https://tailwindcss.com

[+] madoublet|6 years ago|reply
Adam is an awesome follow on Twitter. I still haven't come around to his way of thinking on utility classes. It feels messy compared to a component based approach. But, I totally respect his work and can see myself adopting something like this in the future.
[+] ChrisMarshallNY|6 years ago|reply
I've been writing sites like this for years.

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.

[+] z3t4|6 years ago|reply
You can have many classes. So in your markup you can have an item that is both class article and class bio. Then on another page that is the same but different the class can be article preview. Then in the CSS you make rules for .article and where bio and preview differs you use .article.bio or .article.preview respectively.

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.

[+] nailer|6 years ago|reply
This very long article leads to the following point:

---

> 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.