Bugcrowd design system:
Our Sass guidelines
This version of the documentation is locked from receiving updates. We’re improving it as part of a wider site restructure. Please check back soon.
Sass is pretty awesome.
This section is about harnessing that awesomeness while keeping our styling code
sane, maintainable, and DRY.
A lot of this material coincides with advice set out in the
Sass Guidelines Project. That
style guide goes well beyond what’s covered here; it’s a worthwhile read.
Last updated:
Sep 12, 2023
Use BEM as a naming schema for classes.
BEM example
Consider:
.person {}
.person__hand {}
.person--female {}
.person--female__hand {}
.person__hand--left {}
Nested child blocks of a block are defined using a double underscore.
Variations on a block use hyphens. For example, .button
would be the base
style, with .button--error
and .button--success
as modifiers.
The block should hold all the necessary styling for that block, whereas the
modifier selector should only add or override its necessary styles, ideally in
explicit/long-form.
Note: nested blocks are flattened — we don’t go overboard.
Don’t do this:
.parent-block__child-block__element { }
Do this:
.parent-block__element { }
This is still verbose enough to denote that the class expects to be nested
inside .parent-block
).
Further reading
Last updated:
Sep 4, 2023
Selectors should be written as flat as possible, avoiding any Sass nesting unless explicitly needed to increase specificity.
Nesting — while powerful, and a main feature of Sass — can produce very
specific, long output CSS selectors.
When avoided unless necessary, and followed alongside a consistent class naming
scheme, the CSS’ cascading becomes predictable, and in turn lowers the need for
!important
throughout the codebase.
Example of flatter Sass nesting using BEM
Do this:
// .bc-body comes from Foundations > Body.
.bc-body {
// styles…
a {
text-decoration: underline;
}
}
// Your partial:
.block { }
.block__element { }
.block__element--modifier {
// styles…
.bc-body a & {
text-decoration: none;
}
}
Don’t do this:
// .bc-body comes from Foundations > Body.
.bc-body {
// styles…
a {
text-decoration: underline;
}
}
// Your partial:
.block {
// styles…
.block__element {
// styles…
.block__element--modifier {
// styles…
a {
text-decoration: none !important;
}
}
}
}
Last updated:
Sep 4, 2023
Write class-based selectors.
The design system uses classes, sometimes with nested selectors for specific
HTML elements.
Do not use IDs in selectors, even when required for some specific purpose (eg
linking). There are legitimate use cases for giving an element an ID, eg
<main>
and the primary <nav>
often have IDs so that we can link to them.
In such cases add a class to the element that required the ID, and use it
in selector — even if it shares the same as the ID.
Example of class-based selectors
Do this:
.main-content { }
.main-navigation { }
Don’t do this:
#main-content { }
#main-navigation { }
IDs raise specificity unnecessarily and their usage scales poorly in medium+
sized codebases.
Last updated:
Sep 4, 2023
Short-form notation is fine, but prefer explicit, long-form property notation for overrides.
Short-form notation is A-OK when setting a number of things, but be careful.
Long-form is more explicit/declarative, and should be favoured to avoid
unintentionally overriding a style, or adding something that will later cascade
undesirably to a child.
Example of long-form over short-form notation
Imagine we’ve got styling for .foo
:
.foo {
background:
url(pattern.jpg) /* -image */
red; /* -color */
margin: 0 auto;
}
…and then a bit later we want to add a BEM modifier for the .foo
block, say
.foo--variant
where we want to change the background image, its background
color, and the top margin:
Don’t do this:
.foo--variant {
background-color: blue:
background: url(texture.jpg);
margin: 1em auto 0 auto
}
Do this:
.foo--variant {
background-color: blue:
background-image: url(texture.jpg);
margin-top: 1em;
}
If you use the first --variant
code block, what background color is .foo
?
Transparent.
Why? Because the short-form overrides the more explicit background-color
property. This might not be a problem for us, and if it is, one solution could
be to simply put background-color
after the background
property, but even
cleaner would be to agree that all overrides are explicitly written in long
form.
Of course, if all four margins are being reset, then using the margin
short-form is entirely fine.
Last updated:
Sep 4, 2023
Avoid hand-writing vendor prefixes.
There are experimental [vendor] prefixes, and experimental non-standard
prefixes.
Hand-writing and updating vendor prefixes quickly becomes unmaintainable.
An autoprefixer handles automatic addition/substitution at Sass
compilation of the properties that have support in current browsers.
Don’t do this:
.foo {
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
}
Do this:
.foo {
appearance: none;
}
Let the autoprefixer do the work for us.
Exceptions
Where rare exceptions are necessary, comment why the vendor prefix is added,
eg:
.foo {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
// text-size-adjust is non-standard, but implemented,
// and improves readability.
a {
text-decoration: underline;
webkit-text-decoration-skip: objects;
// text-decoration-skip is non-standard, but implemented,
// and improves legibility of descender glyphs with
// text-decoration: underline.
}
}
Last updated:
Sep 4, 2023
Use @extend
wisely; opt for mixins instead.
Use mixins (mixins without arguments are legit) for reusable code snippets.
Avoid the use of @extend
unless a relationship needs to be made
explicit.
Example of the problem with @extend
The issue is that @extend
will indiscriminately extend every instance of a
matching selector that comes across. For example:
Don’t do this:
.foo { color: red; }
.footer .foo { font-weight: bold; }
.bar { @extend .foo; }
You might have been expecting that to compile into:
.foo, .bar { color: red; }
.footer .foo { font-weight: bold; }
But because @extend
establishes relationships you will find you instead get:
.foo, .bar { color: red; }
.footer .foo, .footer .bar {
font-weight: bold;
}
This can result in absolutely enormous selector strings (aka ‘selector
explosion’).
Instead use selector placeholders or ‘silent classes’ (%foo
) like in the
example below…
.foo,
%foo {
color: red;
}
.footer .foo {
font-weight: bold;
}
.bar {
@extend %foo;
}
…or better yet, just use mixins.
For more information pop over to the Sass Guidelines Project entry on @extend
.
Last updated:
Sep 4, 2023
Document (almost) all the things, using the SassDoc format commenting format.
Quoting from the Sass Guidelines:
CSS is a tricky language, full of hacks and oddities. Because of this, it
should be heavily commented, especially if you or someone else intend to read
and update the code 6 months or 1 year from now. Don’t let you or anybody else
be in the position of I-didn’t-write-this-oh-my-god-why.
Generally:
-
Title and briefly describe any non-trivial declaration blocks with C-style
comments, eg:
**
* Block, function, mixin, ... title
*/
-
Document functions and mixins using the global
SassDoc format using inline comments
with an extra slash (///
), eg:
/// Replace a string with a string
/// http://codepen.io/jakob-e/pen/doMoML
///
/// @author @eriksen_dk <https://twitter.com/eriksen_dk>
///
/// @param {string} $string - The haystack string to be manipulated
/// @param {string} $search - The needle to be replace
/// @param {string} $replace - The replacement
///
/// @return {string} - The manipulated string with replaced values
- Space declaration blocks (unless nested) with two blank lines.
- When documenting specific properties or values, use stock-standard inline
comments (
//
) on the same line if your comment fits within 80 characters for
that line (including the code), or on the following line.
Last updated:
Sep 4, 2023
Write mobile-first responsive styles, with media queries inside selectors.
Mobile-first
A webpage is ‘mobile-first’ if the page loads well for mobile viewports when all
media queries are disabled or removed; the remaining selectors provide styling
for small viewports first.
Where possible write mobile-first styles, and then layer tablet and desktop
styles ‘on top’ through media queries.
This approach is superior to its opposite, where media query rules contain
tablet and mobile styling, effectively undoing the (default) desktop styles,
since the latter is usually more complex than mobile styles.
Nest responsive styles inside what it modifies
When adding media queries, nest them inside the selector that is being
modified. This keeps related styles together.
Example of mobile-first styles
Do this:
.bc-body--guide {
margin: 3em 1.5em; // mobile (default)
// ≥768px: let’s pad sides a bit more
@include bc-media(sm) {
margin-left: 4em;
margin-right: 4em;
}
// ≥992px: now center the page design
@include bc-media(md) {
max-width: 52em;
margin-left: auto;
margin-right: auto;
}
}
Don’t do this:
.bc-body--guide {
max-width: 52em;
margin: 3em auto 1.5em auto;
}
@include bc-media(sm) {
.bc-body--guide {
margin-left: 4em;
margin-right: 4em;
max-width: none; // needs resetting…
}
}
@include bc-media(xs) {
.bc-body--guide {
margin-left: 1.5em;
margin-right: 1.5em;
}
}
Last updated:
Sep 4, 2023
This version of the documentation is locked from receiving updates. We’re improving it as part of a wider site restructure. Please check back soon.
Sass is pretty awesome.
This section is about harnessing that awesomeness while keeping our styling code
sane, maintainable, and DRY.
A lot of this material coincides with advice set out in the
Sass Guidelines Project. That
style guide goes well beyond what’s covered here; it’s a worthwhile read.