Below is a list of linters supported by scss-lint
, ordered alphabetically.
- BangFormat
- BorderZero
- ColorKeyword
- Comment
- Compass Linters
- DebugStatement
- DeclarationOrder
- DuplicateProperty
- ElsePlacement
- EmptyLineBetweenBlocks
- EmptyRule
- FinalNewline
- HexLength
- HexNotation
- HexValidation
- IdSelector
- ImportPath
- Indentation
- LeadingZero
- MergeableSelector
- NameFormat
- NestingDepth
- PlaceholderInExtend
- PropertySortOrder
- PropertySpelling
- QualifyingElement
- SelectorDepth
- SelectorFormat
- Shorthand
- SingleLinePerProperty
- SingleLinePerSelector
- SpaceAfterComma
- SpaceAfterPropertyColon
- SpaceAfterPropertyName
- SpaceBeforeBrace
- SpaceBetweenParens
- StringQuotes
- TrailingSemicolon
- TrailingZero
- UnnecessaryMantissa
- UnnecessaryParentReference
- UrlFormat
- UrlQuotes
- VendorPrefixes
- ZeroUnit
Reports when you use improper spacing around !
(the "bang") in !important
and !default
declarations.
You can prefer a single space or no space both before and after the !
.
Bad
color: #000!important;
Good
color: #000 !important;
Configuration Option | Description |
---|---|
space_before_bang |
Whether a space should be present before the ! , as in color: #000 !important; (default true) |
space_after_bang |
Whether a space should be present after the ! , as in color: #000 ! important; (default false) |
Prefer border: 0
over border: none
.
Prefer hexadecimal color codes over color keywords.
Bad: color keyword
color: green;
Good: hexadecimal color
color: #0f0;
Defining colors directly in properties is usually a smell. When you color your body text in a number of places, if you ever want to change the color of the text you'll have to update the explicitly defined color in a number of places, and finding all those places can be difficult if you use the same color for other elements (i.e. a simple find/replace may not always work).
A better approach is to use global variables like $color-text-body
and refer
to this variable everywhere you want to use it. This makes it easy to update
the color, as you only need change it in one place. It is also more
intention-revealing, as seeing the name $color-text-body
is more descriptive
than #333
or black
. Using color keywords can obfuscate this, as they look
like variables.
Prefer //
comments over /* ... */
.
Bad
/* This is a comment that gets rendered */
Good
// This comment never gets rendered
//
comments should be preferred as they don't get rendered in the final
generated CSS, whereas /* ... */
comments do.
Furthermore, comments should be concise, and using /* ... */
encourages multi-line comments which tend to not be concise.
scss-lint
includes a set of linters for codebases which use the
Compass framework.
###» Compass Linters Documentation
Reports @debug
statements (which you probably left behind accidentally).
Rule sets should be ordered as follows: @extend
declarations, @include
declarations without inner @content
, properties, @include
declarations
with inner @content
, then nested rule sets.
Bad
.fatal-error {
p {
...
}
color: #f00;
@extend %error;
@include message-box();
}
Good
.fatal-error {
@extend %error;
@include message-box();
color: #f00;
p {
...
}
}
The @extend
statement functionally acts like an inheritance mechanism,
which means the properties defined by the placeholder being extended are
rendered before the rest of the properties in the rule set.
Thus, declaring the @extend
at the top of the rule set reminds the
developer of this behavior.
Placing @include
declarations without inner @content
before properties
serves to group them with @extend
declarations and provides the opportunity
to overwrite them later in the rule set.
@include
s with inner @content
often involve @media
rules that rely on
the cascade or nested rule sets, which justifies their inclusion after
regular properties.
Mixin @content
and nested rule sets are also linted for declaration order.
Reports when you define the same property twice in a single rule set.
Bad
h1 {
margin: 10px;
text-transform: uppercase;
margin: 0; // Second declaration
}
Having duplicate properties is usually just an error. However, they can be used
as a technique for dealing with varying levels of browser support for CSS
properties. In the example below, some browsers might not support the rgba
function, so the intention is to fall back to the color #fff
.
.box {
background: #fff;
background: rgba(255, 255, 255, .5);
}
In this situation, using duplicate properties is acceptable, but the linter won't be able to deduce your intention, and will still report an error.
If you've made the decision to not support older browsers, then this lint is
more helpful since you don't want to clutter your CSS with fallbacks.
Otherwise, you may want to consider disabling this check in your
.scss-lint.yml
configuration.
Place @else
statements on the same line as the preceding curly brace.
Bad
@if {
...
}
@else {
...
}
Good
@if {
...
} @else {
...
}
This will ignore single line @if
/@else
blocks, so you can write:
@if { ... } @else { ... }
You can prefer to enforce having @else
on its own line by setting the style
configuration option to new_line
.
Configuration Option | Description |
---|---|
style |
same_line or new_line (default same_line ) |
Separate rule, function, and mixin declarations with empty lines.
Bad: no lines separating blocks
p {
margin: 0;
em {
...
}
}
a {
...
}
Good: lines separating blocks
p {
margin: 0;
em {
...
}
}
a {
...
}
By default, this will ignore single line blocks, so you can write:
.icon-chevron-up { &:before { content: "\e030"; } }
.icon-chevron-down { &:before { content: "\e031"; } }
.icon-chevron-left { &:before { content: "\e032"; } }
.icon-chevron-right { &:before { content: "\e033"; } }
Configuration Option | Description |
---|---|
ignore_single_line_blocks |
Don't enforce for single-line blocks (default true) |
Reports when you have an empty rule set.
.cat {
}
Files should always have a final newline. This results in better diffs when adding lines to the file, since SCM systems such as git won't think that you touched the last line.
You can customize whether or not a final newline exists with the present
option.
Configuration Option | Description |
---|---|
present |
Whether a final newline should be present (default true) |
You can specify whether you prefer shorthand or long-form hexadecimal
colors by setting the style option to short
or long
, respectively.
short
color: #f2e;
long
color: #ff22ee;
Configuration Option | Description |
---|---|
style |
Prefer short or long (default short) |
Checks if hexadecimal colors are written in lowercase. You can specify which
case with the style
option.
Configuration Option | Description |
---|---|
style |
Prefer lowercase or uppercase (default lowercase) |
Ensure hexadecimal colors are valid (either three or six digits).
Bad
p {
background: #ab; // Clearly a typo
}
Good
p {
background: #abc;
}
Avoid using ID selectors.
Bad: highly-specific styling for a single element via ID
#submit-button {
...
}
Good: reusable class
.submit-button {
...
}
While the CSS specification allows for multiple elements with the same ID to appear in a single document, in practice this is a smell. ID selectors should never be used for the purposes of styling an element, as it leads to overly specific styles that aren't easily shared with other elements.
The basenames of @import
ed SCSS partials should not begin with an underscore
and should not include the filename extension.
Bad
@import "foo/_bar.scss";
@import "_bar.scss";
@import "_bar";
@import "bar.scss";
Good
@import "foo/bar";
@import "bar";
You can configure this linter to instead ensure that you do include the
leading underscore or the filename extension by setting either option to
true
. Being explicit might have its place, as long as you are consistent.
@import
declarations that Sass compiles directly into CSS @import
rules
will be ignored.
Configuration Option | Description |
---|---|
leading_underscore |
false or true (default false) |
filename_extension |
false or true (default false) |
Use two spaces per indentation level.
Bad: four spaces
p {
color: #f00;
}
Good: two spaces
p {
color: #f00;
}
You can configure this linter to prefer tabs if you like.
Configuration Option | Description |
---|---|
character |
tab or space (default space) |
width |
Number of character s per indentation level (default 2) |
Don't write leading zeros for numeric values with a decimal point.
Bad: unnecessary leading zero
margin: 0.5em;
Good: no leading zero
margin: .5em;
You can configure this to prefer including leading zeros.
Configuration Option | Description |
---|---|
style |
exclude_zero or include_zero (default exclude_zero) |
Reports when you define the same selector twice in a single sheet.
Bad
h1 {
margin: 10px;
}
.baz {
color: red;
}
// Second copy of h1 rule
h1 {
text-transform: uppercase;
}
Good
h1 {
margin: 10px;
text-transform: uppercase;
}
.baz {
color: red;
}
Combining duplicate selectors can result in an easier to read sheet, but occasionally the rules may be purposely duplicated to set precedence after a rule with the same CSS specificity. However, coding your stylesheets in this way makes them more difficult to comprehend, and can usually be avoided.
You can specify that rule sets which can be nested within another rule
set must be nested via the force_nesting
option, e.g.
Bad
h1 {
color: #fff;
}
h1.new {
color: #000;
}
Good
h1 {
color: #fff;
&.new {
color: #000;
}
}
Configuration Option | Description |
---|---|
force_nesting |
Ensure rule sets which can be nested are nested (default true) |
Functions, mixins, and variables should be declared with all lowercase letters and hyphens instead of underscores.
Bad: uppercase characters
$myVar: 10px;
@mixin myMixin() {
...
}
Good: all lowercase with hyphens
$my-var: 10px;
@mixin my-mixin() {
...
}
Using lowercase with hyphens in CSS has become the de facto standard, and
brings with it a couple of benefits. First of all, hyphens are easier to type
than underscores, due to the additional Shift
key required for underscores on
most popular keyboard layouts. Furthermore, using hyphens in class names in
particular allows you to take advantage of the
|=
attribute selector,
which allows you to write a selector like [class|="inactive"]
to match both
inactive-user
and inactive-button
classes.
The Sass parser automatically treats underscores and hyphens the same, so even if you're using a library that declares a function with an underscore, you can refer to it using the hyphenated form instead.
Depending on whether you use underscores to denote private functions within your
code, you can set the allow_leading_underscore
option (enabled by default)
which will ignore leading underscores in names if they exist, allowing
declarations like @function _private-function() { ... }
.
You can also prefer the BEM convention by setting the
convention
option to BEM
. Any other value will be treated as a regex.
Configuration Option | Description |
---|---|
allow_leading_underscore |
Whether to allow names to start with a single underscore (default true ) |
convention |
Name of convention to use (hyphenated_lowercase (default) or BEM ), or a regex the name must match |
Avoid nesting selectors too deeply.
Bad: deeply nested
.one {
.two {
.three {
.four {
...
}
}
}
}
Good
.three:hover {
}
.three {
&:hover {
...
}
}
Overly nested rules will result in over-qualified CSS that could prove hard to maintain, output unnecessary selectors and is generally considered bad practice.
This linter will not report an error if you have selectors with a large depth of applicability. Use SelectorDepth for this purpose.
No error
.one .two .three {
...
}
Error
.one {
.two {
.three {
...
}
}
}
Configuration Option | Description |
---|---|
max_depth |
Maximum depth before reporting errors (default 3) |
Always use placeholder selectors in @extend
.
Bad: extending a class
.fatal {
@extend .error;
}
Good: extending a placeholder
.fatal {
@extend %error;
}
Using a class selector with the @extend
statement statement usually results
in more generated CSS than when using a placeholder selector. Furthermore,
Sass specifically introduced placeholder selectors in order to be used with
@extend
.
See Mastering Sass extends and placeholders.
Sort properties in a strict order. By default, will require properties be
sorted in alphabetical order, as it's brain dead simple (highlight lines and
execute :sort
in vim
), and it can
benefit gzip compression.
You can also specify an explicit ordering via the order
option, which allows
you to specify an explicit array of properties representing the preferred
order, or the name of a
preset order.
If a property is not in your explicit list, it will be placed at the bottom of
the list, disregarding its order relative to other unspecified properties.
For example, to define a custom sort order, you can write:
linters:
PropertySortOrder:
order:
- display
- margin
- etc...
Or you can use a preset order by writing:
linters:
PropertySortOrder:
order: concentric
If you need to write vendor-prefixed properties, the linter will allow you to order the vendor-prefixed properties before the standard CSS property they apply to. For example:
border: 0;
-moz-border-radius: 3px;
-o-border-radius: 3px;
-webkit-border-radius: 3px;
border-radius: 3px;
color: #ccc;
margin: 5px;
In this case, this is usually avoided by using mixins from a framework like Compass or Bourbon so vendor-specific properties rarely need to be explicitly written by hand.
If you are specifying an explicit order for properties, note that vendor-prefixed properties will still be ordered based on the example above (i.e. you only need to specify normal properties in your list).
Configuration Option | Description |
---|---|
order |
Array of properties, or the name of a preset order (default is nil , resulting in alphabetical ordering) |
ignore_unspecified |
Whether to ignore properties that are not explicitly specified in order (default false) |
Reports when you use an unknown CSS property (ignoring vendor-prefixed properties).
diplay: none; // "display" is spelled incorrectly
Since the list of available CSS properties is constantly changing, it's
possible that you might get some false positives here, especially if you're
using experimental CSS features. If that's the case, you can add additional
properties to the whitelist by adding the following to your .scss-lint.yml
configuration:
linters:
PropertySpelling:
extra_properties:
- some-experimental-property
- another-experimental-property
If you're sure the property in question is valid, submit a request to add it to the default whitelist.
Configuration Option | Description |
---|---|
extra_properties |
List of extra properties to allow |
Avoid qualifying elements in selectors (also known as "tag-qualifying").
Bad: qualifying elements
div#thing {
...
}
ul.list {
...
}
ul li.item {
...
}
a[href="place"] {
...
}
Good
#thing {
...
}
.list {
...
}
ul .item {
...
}
[href="place"] {
...
}
Since IDs are unique, they will not apply to multiple elements, so there is no good reason to qualify an ID selector with an element.
In most cases, qualifying a class or attribute selector with an element adds unnecessary or undesirable specificity. Often the element qualifier is already superfluous; and if it is not, you will probably be better off refactoring so that it can be removed.
Use the options to allow certain qualifying elements.
Configuration Option | Description |
---|---|
allow_element_with_attribute |
Allow elements to qualify attributes (default false) |
allow_element_with_class |
Allow elements to qualify classes (default false) |
allow_element_with_id |
Allow elements to qualify ids (default false) |
Don't write selectors with a depth of applicability greater than 3.
Bad: selectors with depths of 4
.one .two .three > .four {
...
}
.one .two {
.three > .four {
...
}
}
Good
.one .two .three {
...
}
.one .two {
.three {
...
}
}
Selectors with a large depth of applicability lead to CSS tightly-coupled to your HTML structure, making it brittle to change.
Deep selectors also come with a performance penalty, which can affect rendering times, especially on mobile devices. While the default limit is 3, ideally it is better to use less than 3 whenever possible.
Configuration Option | Description |
---|---|
max_depth |
Maximum depth before reporting errors (default 3) |
It is good practice to choose a convention for naming selectors.
Good
// convention: 'hyphenated_lowercase'
.foo-bar-77, foo-bar, #foo-bar {}
// convention: 'snake_case'
.foo_bar77, foo_bar, #foo_bar {}
// convention: 'camel_case'
.fooBar77, fooBar, #fooBar {}
}
You can specify different conventions for different types of selectors using the [type]_convention
options.
Since you might need to overwrite selectors for third party stylesheets, you
can specify ignored_names
as an array of individual selectors to ignore.
Another option is to specify ignored_types
to globally ignore a certain
type of selector.
Configuration Option | Description |
---|---|
convention |
Name of convention to use (hyphenated_lowercase (default) or snake_case , camel_case , or BEM , or hyphenated_BEM ), or a regex the name must match |
ignored_names |
Array of whitelisted names to not report lints for. |
ignored_types |
Array containing list of types of selectors to ignore (valid values are attribute , class , element , id , placeholder ) |
attribute_convention |
Convention for attribute selectors only. See the convention option for possible values. |
class_convention |
Convention for class selectors only. See the convention option for possible values. |
id_convention |
Convention for id selectors only. See the convention option for possible values. |
placeholder_convention |
Convention for placeholder selectors only. See the convention option for possible values. |
Prefer the shortest shorthand form possible for properties that support it.
Bad: all 4 sides specified with same value
margin: 1px 1px 1px 1px;
Good: equivalent to specifying 1px for all sides
margin: 1px;
Properties within rule sets should each reside on their own line.
Bad
p {
margin: 0; padding: 0;
}
Good
p {
margin: 0;
padding: 0;
}
A special exception is made for single line rule sets. For example the following is acceptable:
p { margin: 0; padding: 0; }
If you want to also report a lint for single line rule sets, set the
allow_single_line_rule_sets
option to false
.
Configuration Option | Description |
---|---|
allow_single_line_rule_sets |
true or false (default true) |
Split selectors onto separate lines after each comma.
Bad: comma-separated selectors not on their own lines
.error p, p.explanation {
...
}
Good: each selector sequence is on its own line
.error p,
p.explanation {
...
}
Note that selectors containing interpolation are ignored, since the Sass parser
cannot construct the selector parse tree at parse time, only at run time (which
is too late for scss-lint
to do anything with).
Commas in lists should be followed by a space.
Bad: no space after commas
@include box-shadow(0 2px 2px rgba(0,0,0,.2));
color: rgba(0,0,0,.1);
Good: commas followed by a space
@include box-shadow(0 2px 2px rgba(0, 0, 0, .2));
color: rgba(0, 0, 0, .1);
Properties should be formatted with a single space separating the colon from the property's value.
Bad: no space after colon
margin:0;
Bad: more than one space after colon
margin: 0;
Good
margin: 0;
The style
option allows you to specify a different preferred style.
Configuration Option | Description |
---|---|
style |
one_space , no_space , at_least_one_space , or aligned (default one_space) |
Properties should be formatted with no space between the name and the colon.
Bad: space before colon
margin : 0;
Good
margin: 0;
Opening braces should be preceded by a single space.
Bad: no space before brace
p{
...
}
Bad: more than one space before brace
p {
...
}
Good
p {
...
}
Setting allow_single_line_padding
to true
allows you to use extra spaces to
nicely align single line blocks, so you can write:
.icon-chevron-up { &:before { content: "\e030"; } }
.icon-chevron-down { &:before { content: "\e031"; } }
.icon-chevron-left { &:before { content: "\e032"; } }
.icon-chevron-right { &:before { content: "\e033"; } }
Set style
to new_line
if you prefer to use a new line before braces, rather than a single space.
p
{
...
}
Configuration Option | Description |
---|---|
allow_single_line_padding |
Allow single line blocks to have extra spaces for nicer formatting (default false) |
style |
space or new_line (default space ) |
Parentheses should not be padded with spaces.
Bad
@include box-shadow( 0 2px 2px rgba( 0, 0, 0, .2 ) );
color: rgba( 0, 0, 0, .1 );
Good
@include box-shadow(0 2px 2px rgba(0, 0, 0, .2));
color: rgba(0, 0, 0, .1);
Configuration Option | Description |
---|---|
spaces |
Spaces to require between parentheses (default 0) |
String literals should be written with single quotes unless using double quotes would save on escape characters.
Bad: double quotes
content: "hello";
Good: single quotes
content: 'hello';
Good: double quotes prevent the need for escaping single quotes
content: "'hello'";
Single quotes are easier to type by virtue of not requiring the Shift
key on
most popular keyboard layouts.
Configuration Option | Description |
---|---|
style |
single_quotes or double_quotes (default single_quotes ) |
Property values; @extend
, @include
, and @import
directives; and variable
declarations should always end with a semicolon.
Bad: no semicolon
p {
color: #fff
}
Bad: space between value and semicolon
p {
color: #fff ;
}
Good
p {
color: #fff;
}
CSS allows you to omit the semicolon if the statement is the last statement in the rule set. However, this introduces inconsistency and requires anyone adding a property after that property to remember to append a semicolon.
Don't write trailing zeros for numeric values with a decimal point.
Bad: unnecessary trailing zero
margin: .500em;
Good: no trailing zero
margin: .5em;
The extra zeros are unnecessary and just add additional bytes to the resulting generated CSS.
Numeric values should not contain unnecessary fractional portions.
Bad
margin: 1.0em;
Good
margin: 1em;
Sass will automatically convert integers to floats when necessary, making the use of a fractional component in a value to "force" it to be a floating point number unnecessary. For example, the following code:
$margin: 1;
p { margin: $margin / 2; }
...will compile to:
p { margin: 0.5; }
Do not use parent selector references (&
) when they would otherwise be
unnecessary.
Bad
.foo {
& > .bar {
...
}
}
Good
.foo {
> .bar {
}
}
URLs should not contain protocols or domain names.
Including protocols or domains in URLs makes them brittle to change, and also unnecessarily increases the size of your CSS documents, reducing performance.
Bad: protocol and domain present
background: url('https://example.com/assets/image.png');
Good
background: url('assets/image.png');
URLs should always be enclosed within quotes.
Bad: no enclosing quotes
background: url(example.png);
Good
background: url('example.png');
Using quoted URLs is consistent with using other Sass asset helpers, which also expect quoted strings. It also works better with most syntax highlighters, and makes it easier to escape characters, as the escape rules for strings apply, rather than the different set of rules for literal URLs.
See the URL type documentation for more information.
Avoid vendor prefixes. That is, don't write them yourself.
Instead, you can use Autoprefixer or mixins -- such as Compass or Bourbon -- to add vendor prefixes to your code. (If using your own mixins, make sure to exempt their source from this linter.)
At-rules, selectors, properties, and values are all checked. (See the examples below.)
The default identifier_list
, base
, should include everything that Autoprefixer addresses. You could also use a list covering Bourbon's CSS3 mixins: bourbon
. If neither of those suit you, you can write your own identifier list.
Additionally, you can manually include or exclude identifiers from the identifier list -- if, for example, you want to use pretty much all of the base
list but also want to allow yourself to use vendor prefixed transform
properties, for one reason or another.
(All identifiers used by the identifier_list
, include
, or exclude
are stripped of vendor prefixes. See the predefined lists for examples.)
Bad: vendor prefixes
@-webkit-keyframes anim {
0% { opacity: 0; }
}
::-moz-placeholder {
color: red;
}
.foo {
-webkit-transition: none;
}
.bar {
position: -moz-sticky;
}
Good
// With Autoprefixer ...
@keyframes anim {
0% { opacity: 0; }
}
::placeholder {
color: red;
}
.foo {
transition: none;
}
.bar {
position: sticky;
}
// With Bourbon mixin
@include placeholder {
color: red;
}
Configuration Option | Description |
---|---|
identifier_list |
Name of predefined identifier list to use (base or bourbon ) or an array of identifiers (default base ) |
include |
Identifiers to lint, in addition to the identifier_list (default [] ) |
exclude |
Identifers in the identifier_list to exclude from linting (default [] ) |
Omit length units on zero values.
Bad: unnecessary units
margin: 0px;
Good
margin: 0;
Zero is zero regardless of the units of length.
Note that this only applies to lengths, since it is invalid to omit units for other types such as angles or times.