zvint’s sass code guidelines
Sass is a relatively small but extremely powerful scripting language in order to style web components. It is also very popular, which makes up for a lot of diversity, especially when it comes to whether one should even use a CSS preprocessor or opt-in for alternatives (like CSS in JS).
If you are one of the majority of people that like having separation of concerns by dividing your app and style, it must be understood that it is as important to have conventions for the code of your app, as well as for your styling. Having inconsistent code-styles may not harm or interest your workflow, but it helps a ton when collaborating with others - and also if you come back later to inspect your old code.
These guidelines, as simple as they seem, will speed up your efficiency, help you to find bugs significantly and make your code look better. Well, let’s get into it, shall we?
Does this guide have opinions about CSS?
This guideline has no explicit opinion about CSS. It is primarily opinionated about Sass and only contains a few points that also apply to CSS in Sass.
Is this guide about SCSS or Sass?
This document contains guidelines for Sass and SCSS. SCSS and “the indented syntax” are understood under “Sass” and it is explicitly mentioned if only SCSS or “the indented syntax” is targeted.
What syntax is preferred?
SCSS. The examples in this guide are all written in the SCSS syntax. Please refer to the official Sass syntax docs to learn how to translate the examples to the indented syntax.
Naming conventions give us a lot of advantages and are necessary when it comes to modern day software development. Naming conventions give us consistency, help us debug our code and help us use and name things, which, as we all know, is the hardest part of programming.
$_fooBar $pFooBar $fooBar $_FooBar $_FOO_BAR _foo_bar() foo-bar() foo_bar
Jokes aside, you will be able to intuitively recognize and distinguish the usages of every item in the example above after understanding the following conventions.
Since there already is a language-specific distinction between a mixin and function, both are named the same way. Although both are named with the same schema, there is a (also native) distinction between a private and public function or mixin.
The reason we separate the naming convention of private and public functions and mixins is to immediately show the user a distinct difference in their visibility.
Functions that are accessible publicly are written in lower kebab-case
.
@function my-public-function()
// or as a mixin
@mixin my-public-mixin()
Functions and mixins that are supposed to be private are prefixed with an underscore (_
), followed by lower snake_case
.
@function my_private_function()
// or as a mixin
@mixin _my_private_mixin()
Every parameter that is not a variable-argument is prefixed with p
followed by PascalCase
,
@function my-function($pFoo, $pBar)
whereas a variable-argument is prefixed with va
and also followed by PascalCase
.
@function my-function($vaFooBars...)
A locally declared variable that is not a parameter is prefixed with an underscore (_
) and followed by camelCase
.
// ... local scope
$_localVariable: fooBaz;
Globally declared variables have two general ways to be written.
Variant 1: style-specific globals
A global variable that can be applied to a css-property and is intended to be style-specific is written in kebab-case
, using a hyphen (-
) to indicate its private accessibility.
$background-color: red;
$-background-color: blue;
Keeps it consistent with public- and different with private functions and mixins. This also is compliant with most css namings and helps users to understand the styling-purpose.
Variant 2: non-style-specific globals
A global variable that is not applicable to a css-property or not intended to be style-specific is written differently depending on its accessibility.
A public not style-specific global is written in camelCase
.
$myPublicGlobalMap: ()
A private not style-specific global is written in PascalCase
with an underscore (_
) indicating its private accessibility.
$_MyPrivateGlobalMap: ()
Conclusion of both variants
Both variants are really useful to immediately show a user the purpose and intention of a global. Global variables that are meant for a sole style purpose are treated differently to variables that have no initial intention for styling or cannot be applied to a css-property directly.
If a privately declared global is meant to be a constant, it is written in upper SNAKE_CASE
. The variable may not be reassigned after the variable is declared once.
⚠️ There is no physical constness as of
Dart Sass 1.57
, so there is no guarantee a variable is never modified after its initial declaration. It just serves as a recommendation.
This convention really is only applicable if a global is private, since any external
acquiring the variable may override or oversee the intended logical constness.
// ... global scope
$_GLOBAL_PRIVATE_CONSTANT: fooBar;
The namespace of a module must always be in snake_case
. Since the namespace of a module is implicitly its filename without extension*, the casing also applies for the name of every file that is intended to be used as a Sass module in other style files.
The above convention also applies for when you define an explicit namespace using an import statement such as @use <url> as <namespace>
, where the implicit namespace is replaced by the explicit for the scope it is imported in.
*
By default, a module’s namespace is just the last component of its URL without a file extension
This section provides code-style conventions for both Sass syntaxes.
Every line has a maximum character limit of 95.
The maximum number of parameters is indefinite, but from a visual perspective a function or mixin should not have more than 6 (six) individually named parameters in its signature.
If your code requires you to pass more parameters than is recommended, reconsider your implementation.
If a string that is not a URL contains no whitespace character, it is wrapped in single-quotes ('...'
). Otherwise, a string is wrapped in double-quotes ("..."
).
Imports are done with double-quotes, since they use a URL.
@use "foo_bar" as baz_qux;
$sentence: "hello world";
$word: 'fooBar';
If a string contains interpolation but no whitespace character, single-quotes are used, even if the interpolation contains whitespace.
$sentenceWithPeriod: '#{$sentence}.';
Each substatement of a statement should be indented by 2 (two) whitespace characters.
@if (foo) {
@if (bar) {
// ...
}
}
Line wrap for maps
In a map with more than 2 (two) key-value pairs, each key-value pair should be put on their own line followed by a comma at the end of each line, except for the last key-value pair.
$someGlobalMap: (
hello: world,
foo: bar,
baz: qux
);
Line wrap for lists
In a list with more than 5 (five) elements, insert a newline character followed by the necessary indentation every five elements, or at whenever the line length would exceed the specified character limit.
$someGlobalList: (
"foo", "bar", "foobar", "baz", "qux",
"quux", "lorem", "dolor", "hello", "world"
);
Although this usage of list-wrapping is recommended, it is pretty loose, so you don’t have to follow this one-to-one in order to follow this guideline.
There must not be more than 4 (four) nested scopes.
// ⚠️ BAD:
.selector {
@if (!foo) {
@if (!bar) {
@if (!baz) {
@if (!qux) { // <-- too much
// ...
// ✅ GOOD:
.selector {
@if (!foo and !bar and !baz and !qux) {
// or:
@if (not (foo or bar or baz or qux)) {
// ...
If a semicolon can be placed after a statement, it shall be done. More formally, every statement that is not a control flow statement (i.e. end with a scope) must end with a semicolon (;
) and a newline character.
// ⚠️ BAD:
@function my-function() {
@return 'myValue'
}
// ✅ GOOD:
@function my-function() {
@return 'myValue';
// ^
}
Why? It makes your code more consistent, easier to read, debug and maintain.
Well that’s a wrap! I hope you like this guideline.
If you have suggestions you can DM me on Discord: @boned#0937