CSS structures - BEM vs Tailwind vs Atomic vs Utility
CSS is terrible (or not?)
Many backend engineer think CSS is a terrible thing (many disagree that it’s a programming language, thus just calling it a thing). And there are countless memes about how terrible CSS is.
It mostly comes down to:
- CSS is too flexible: you can achieve a layout in many ways in CSS.
- The way CSS is applied: Specificity, inheritance, and global by default make it easy to accidentally break things.
Many brilliant engineers have come up with many ways to solve these problems and make CSS more maintainable. (I didn’t include SMACSS
and OOCSS
, they are more on how to name & organize CSS, a bit different)
BEM
BEM has been around for a while, and widely adopted. It’s pretty straightforward Block__Element--Modifier
(example from BEM documentation):
.form { }
.form--theme-xmas { }
.form--simple { }
.form__input { }
.form__submit { }
.form__submit--disabled { }
<form class="form form--theme-xmas form--simple">
<input class="form__input" type="text" />
<input
class="form__submit form__submit--disabled"
type="submit" />
</form>
Yea:
- Easy to get started
- It works, especially for complex applications with many engineers
Nay:
- Both HTML and CSS are very wordy.
- I have to come up with many names
- I need to be careful that block names has no conflicts (not an issue when used in component based frameworks)
RSCSS
This is a variation of BEM, and I like it a lot. It’s much cleaner than BEM (both HTML and CSS). The same example translated into RSCSS is:
.form {
&.-theme-xmas { }
&.-simple { }
& > .input { }
& > .submit {
&.-disabled { }
}
}
<form class="form -theme-xmas -simple">
<input class="input" type="text" />
<input
class="submit -disabled"
type="submit" />
</form>
Yea:
- Easy to get started, and works
- Cleaner HTML and CSS
Nay:
- It uses nesting, which is not natively supported yet spec draft
- Some people find class names starting with
-
and_
werid
Utilities (Tailwind)
I’m not sure the name “utilities” is accurate, some also call it “atomic”, but I want to distinguish from the next one.
<div class="w-32 bg-gray-100 rounded-xl p-8"><div>
<style>
.w-32 {width: 32px}
.bg-gray-100 {background-color: #ddd}
.rounded-x1 {border-radius: .75em}
.p-8 {padding: 8px}
</style>
It’s easy to get. Basically you can know all the CSS by reading the HTML.
If you think about it, you’ll realize the CSS file can be very complex - you might need to have all of w-32, w-16, w-18, w-8
etc., and adding them one-by-one is tedious. That’s why it’s commonly used with a tool like Tailwind. Tailwind can help you generate those rules and optimize them for you.
Yea:
- Easy to understand css from HTML
- No css rule specificity wars
- Optimized for size (with some tools)
Nay:
- HTML becomes messy. Lost semantic meanings
- One more naming convension to remember
- Not practical without supporting tools
Some argues this is getting back to inline styles - One of the first issues opened on tailwind 😂 . (I agree it’s much better than inline styles)
Checkout how a CSS prank turns out very similar as what Tailwind is Funny CSS to Tailwind
Atomic
(Again, I use the word “atomic” just to distinguish from “utility” above. Many people call the one above “atomic css”. If you know a better name for this type, let me know~)
This is very similar as the one above, but kinda reversed.
<div class="card">
<ul class="list">
<ul class="item"></ul>
<ul class="item"></ul>
<ul class="item"></ul>
</ul>
<button class="open-button"></button>
</di>
<style>
.card,
.list {
padding: 1em;
}
.item,
.open-button {
background: orange;
}
.card
.open-button {
border-radius: 1em;
}
</style>
Yea:
- HTML is easy to read
- No css rule specificity wars
Nay:
- Harder to reason about each element by reading the CSS