Guidelines for good SCSS / CSS
Some guidelines for writing good CSS that scales well and doesn't make your colleagues ragey.
Use low-specificity selectors
The more specific the selector is, the less reusable the accompanying CSS is and/or the longer a selector you need to override it. (Or you add !important
in places you shouldn't.)
The following should be used sparingly in CSS
id
selectors.- Complex element/type selectors with attributes such as
button[type=button]
andinput[type=text]
. - Descendant combinators such as
#main ol li ul
or.process p a
. - Chaining class names.
button
andbutton.lg
)
High specificity selectors are more likely to create side effects, that have to be undone with more CSS rules or longer selectors.
Avoid nesting SCSS selectors prematurely
SCSS compiles nested selectors into descendant combinators. For example:
#content {
ol {
p {}
}
}
Compiles to:
#content ol p {}
There's a high likelihood, however that your selectors don't need to be that long; ol p
would provide the same styling. If the worry is being overly broad, you can get the same results using a class name. This is related to the previous point. Specific selectors are often caused by SCSS nesting.
Nesting selectors can be useful, however, when creating variants. For example:
.button {
&-link {
}
}
Compiles to button-link
.
Restrict class names to a single pattern or component type.
For example, don't use .process
for lists and as a div
or section
type. Rules you introduce for div.process
probably aren't related to those for ol.process
. Instead use .list-process
and .section-process
.
Favor descriptive class names that describe what the class does or the kind of content it affects
Class names such as .primary
, or .section
are confusing and more likely to be misused by a colleague than .intro-text
or .sidebar
.
Use a product-specific prefix to avoid class name collisions
This keeps selector specificity low, while also restricting the side-effects of any one selector.
Don't use @extend
SCSS @extend
repeats every instance of the extended selector for the extendee selector. (This will be flagged by our Sass-lint configuration.)
h4 {
color: #c09;
font-size: 1.2rem;
font-weight: 100;
}
label {
@extend h4;
cursor: pointer;
}
.footer h4 {
display: inline;
}
Compiles to:
h4, label {
color: #c09;
font-size: 1.2rem;
font-weight: 100; }
label {
cursor: pointer; }
.footer h4, .footer label {
display: inline; }
Every instance of h4
will also be applied to label
. This is usually not the behavior we want, particularly across an entire code base.
CHECK. YOUR. OUTPUT.
Periodially check your generated CSS files (JavaScript too!) to ensure that you didn't introduce bloat with your selectors or asset imports.
True story: we reduced the size of our home page CSS by ~400K by removing SVG fonts. Our Webpack configuration included base64-encoded versions of SVG fonts which dramatically inflated our file size. This fact was discovered only after viewing the generated CSS files.