I’ve written a lot of CSS over the past six years. Here’s the opinionated result of all that experience.
In this post:
Code style
For classes, I do lower-kebab-case
. IDs get lower_snake_case
. I’m not too
terribly opinionated about comments, other than preferring to see them above the
code they reference. If a comment line gets too long, I have no problems
wrapping it for readability.
// This class is necessary to prevent reactor meltdown.
.no-go-boom {
// Use `fixed` instead of `sticky` so that it
// always stays in place.
position: fixed;
}
I tend to stick to the “one property: value;
per line” rule, except for those
properties that get pretty gnarly. For example, I use newlines for my gradient
styles:
.background-gradient {
background-image:
linear-gradient(
var(--g-background-gradient-direction),
var(--c-gradient-color1),
var(--c-gradient-color2)
);
}
This helps keep line lengths in check and makes it a little easier (for me, anyway) to consume.
Beyond that, it’s all pretty standard stuff. I keep my selectors one-to-a-line, put semicolons on my final properties, and slap that closing brace on its own line because it deserves it.
Naming conventions
I stick with Harry Roberts’ namespaced
BEM
in most scenarios. I’m a big fan of BEM, and an even bigger fan of how these
namespaces help keep me organized. It’s true: BEM class names can get
loooooooong, but I’ve come to appreciate a few extra characters for the sake of
clarity. We all might be able to understand .btn
, but ain’t nobody getting to
the bottom of .prim-crd-hd-top
(pardon the hyperbolic example 😆).
Lately, however, I have started to pull back from enforcing BEM in full. Dabbling with component-based UI architecture has removed a lot of the need to be so explicit with my class names. Whereas I previously might have written something like…
.c-card { /* card container styles */ }
.c-card__header { /* card header styles */ }
.c-card__content { /* card content styles */ }
.c-card__footer { /* card footer styles */ }
I now opt for something like this:
.c-card { /* card container styles */ }
.c-card > header { /* card header styles */ }
.c-card > div { /* card content styles */ }
.c-card > footer { /* card footer styles */ }
I looked down upon the latter example as pure heresy for the longest time. However, my specific experience in my current role has led me to ease up a little bit on the BEM intensity.
For starters, I’m usually the only one looking at the CSS (and guiding the
accompanying HTML development). So the only person that really gets any
benefit out of seeing <header class="c-card__header">
vs just <header>
is…me. And it turns out that I don’t actually care that much. Targeting
bare elements within a parent CSS class has been working out just fine, and
has actually reduced the number of HTML errors since transitioning away from
full BEM.
I’ve also discovered that for refactoring, you almost always have to touch the
HTML and CSS anyway. Going full BEM might be okay for something as simple as the
.c-card
example above, but some more complex components are not guaranteed to
always have the same pieces with the same relationships when it’s time for an
update. If I have to do at least some of it all over again, I may as well quit
pretending and adopt a strategy that helps my developers write valid HTML.
I’m not trying to sway anyone’s opinion here. I still love BEM and don’t hate the time I spent going whole-hog. But after stepping back to look at the coordination of HTML and CSS and JS, and considering the experience my developers were having trying to work with what I was giving them—this compromise is working out much better so far.
Property order
I order my CSS properties alphabetically, with a twist: properties that depend
on other properties are further alphabetized under the “parent” property. If the
properties map to a part of a short-hand value (like background-attachment
and
background-position
from background
), I alphabetize those as well. If I’m
using a mix of short-hand and explicit properties, I use the shorthand first,
then alphabetize the remaining properties. When the properties reference a
particular side, like border-left
, those are organized in the same clock-wise
fashion CSS uses—top
, right
, bottom
, then left
.
Clear as mud? For example:
.my-cool-demo-class {
background-color: #c0ffee;
color: #123456;
position: absolute;
top: 4rem;
right: 1ch;
bottom: 0;
z-index: 9;
}
.another-one {
border: .125em solid var(--c-body-background);
border-bottom-width: .25em;
border-left-color: var(--c-body-text);
margin: 1em;
margin-top: 0;
}
It’s janky, and not all that approachable for a newbie (or anyone at all?), but
it really helps keep my mind straight. The methods for grouping styles based on
their role (color, then layout, then position, etc) confuse the heck out of me.
I can move a lot quicker by flipping through an alphabetized list (I know we’re
all using search
and goto
here, just humor me). I should probably just get
one of those plugins that automatically alphabetizes things on git commit
🤷♂️.
File organization
The namespaces make this pretty straightforward. Even if I’m writing pure CSS, like on this here blog, I still take advantage of SCSS when offered for at least its concatenation feature.
Every namespace gets a folder, and every folder is filled with SCSS partials
that encapsulate some scope of styles. That could be a collection of small
@function
s, a grouping of loosely-related “global” styles, or a single
component’s worth of CSS. Everything then gets rolled up into one big ol’
manifest that compiles it all down to the requisite .css
file(s).
Compared to my hardcore property ordering approach, I try to stay pretty lax in this area. I let the needs of the project and the reality of the codebase drive its organization.