...but are always worth mentioning, because they're incredibly cool compared to vanilla CSS:
The familiar way:
a {
&:hover {
color: red;
}
}
/* compiled CSS */
a:hover {
color: red;
}
But &
can be used with a prefix just as well:
p {
body.no-touch & {
display: none;
}
}
/* compiled CSS */
body.no-touch p {
display: none;
}
This can be very useful when you have a deep nesting of rules already in place, and you want to effect a change to the styling of an element based on a selector match much closer to the DOM root. Client capability flags such as the no-touch
class are often applied in such a way, to the <body>
element for example.
Variables can be expanded in selectors, too:
$alertClass: "error";
p.message-#{$alertClass} {
color: red;
}
/* compiled CSS */
p.message-error {
color: red;
}
...or almost anywhere else for that matter, like in media queries or CSS comments:
$breakpoint: 768px; // this would likely go to a _settings.scss somewhere
@media (max-width: #{$breakpoint}) {
/* This block only applies to viewports <= #{$breakpoint} wide... */
}
/* compiled CSS */
@media (max-width: 768px) {
/* This block only applies to viewports <= 768px wide... */
}
If your SCSS module can be configured using globals (which tends to be the SCSS way), you can declare them with a default value:
// _my-module.scss:
$message-color: blue !default;
p.message {
color: $message-color;
}
/* compiled CSS */
p.message {
color: blue;
}
But you can then optionally override the module defaults before its inclusion:
$message-color: black;
@import 'my-module';
/* compiled CSS */
p.message {
color: black;
}
That is, an assignment with a !default
will only take effect if such a variable didn't have a value before (in contrast to the standard assignment, which will always overwrite a possible previous value).
This is how many SCSS modules (including most that ship with Compass) are configured.
SCSS sports the standard set of flow control directives, such as if
:
$debug: false; // this would likely be in a _settings.scss somewhere
article {
color: black;
@if ($debug) { // visualizing layout internals
border: 1px dotted red;
}
}
/* compiled CSS */
article {
color: black;
}
Having such compile-time flags in your project's styling can help debug complex layout issues visually, faster than just inspecting the page an element at a time.
There's also @for
, @each
and @while
. They're good for a number of things, like:
@each $name in 'save' 'cancel' 'help' {
.icon-#{$name} {
background-image: url('/images/#{$name}.png');
}
}
/* compiled CSS */
.icon-save {
background-image: url("/images/save.png");
}
.icon-cancel {
background-image: url("/images/cancel.png");
}
.icon-help {
background-image: url("/images/help.png");
}
...and much more. Keep in mind, though, that if you need them in your daily styling work you're probably overdoing it. Instead, they usually warrant the added complexity when building configurable SCSS modules and other such reusable components.
The interested reader can check out the full documentation on the subject.
As we saw in the previous example, @each
can iterate over a list. Lists are in fact a fundamental part of the SCSS language, but a quick demo of their application might be configuring some generated styling:
$buttonConfig: 'save' 50px, 'cancel' 50px, 'help' 100px;
@each $tuple in $buttonConfig {
.button-#{nth($tuple, 1)} {
width: nth($tuple, 2);
}
}
/* compiled CSS */
.button-save {
width: 50px;
}
.button-cancel {
width: 50px;
}
.button-help {
width: 100px;
}
This demonstrates two features of the list data type, namely the nth()
list accessor function, and more interestingly list nestability: in JavaScript notation, the above would be equivalent to:
var buttonConfig = [[ 'save', 50 ], [ 'cancel', 50 ], [ 'help', 100 ]];
That is, lists can be separated by both spaces and commas, and alternation between the two notations produces nested arrays.
Mixins are a well-known part of the language, but SCSS also allows you to define custom functions. Contrary to what one might expect, this can also be done in pure SCSS, instead of extending SCSS in Ruby:
@function make-greener($value) {
@return $value + rgb(0,50,0); // arithmetic with colors is _b
}
p {
background: make-greener(gray);
}
/* compiled CSS */
p {
background: #80b280;
}
The above color is a gray with a slight green tint.
Functions are most useful in avoiding some repeated computation in an expression. It also implicitly documents it by giving it a name. SCSS ships with a ton of useful built-in functions, and Compass adds even more, so do first check whether there's a built-in equivalent before implementing your own helper.
Arguments and functions support default values for arguments; the final 0-N arguments can be made optional by providing them with a default value:
@mixin foobar($a, $b, $padding: 20px) {
padding: $padding;
// ...and something with $a and $b
}
p {
@include foobar(123, "abc"); // the default padding's fine
}
p.important {
@include foobar(123, "abc", 50px); // override the default
}
If your mixin (or function) takes a lot of arguments, there's a similar call-time syntax for selecting only specific arguments to override:
@mixin foobar($topPadding: 10px, $rightPadding: 20px, $bottomPadding: 10px, $leftPadding: 20px, $evenMorePadding: 10px) {
// do something with all these arguments...
}
p {
@include foobar($bottomPadding: 50px); // specify only the argument you want to override
}
In cases where a lot of the arguments are just for overriding specific style properties, however, the "content block overrides -pattern" may work a lot better (see below).
TODO: ...and expanding and/or extending during further calls.
Since version TODO, SCSS has had an implicit mixin argument accessible through the @content
directive. It allows passing an entire SCSS content block as an argument to the mixin:
@mixin only-for-mobile {
@media (max-width: 768px) {
@content;
}
}
@include only-for-mobile() /* content begins */ {
p {
font-size: 150%;
}
} /* content ends */
/* compiled CSS */
@media (max-width: 768px) {
p {
font-size: 150%;
}
}
This is a very powerful feature. You can mix standard and content block arguments, too:
TODO
@mixin only-for-mobile($breakpoint) {
@media (max-width: #{$breakpoint}) {
@content;
}
}
@include only-for-mobile(480px) {
p {
font-size: 150%;
}
}
...
p {
@media (max-width: 768px) {
// Use larger text for smaller screens:
font-size: 150%;
}
}
Compiles into:
/* compiled CSS */
@media (max-width: 768px) {
p {
font-size: 150%;
}
}
p {
@media (max-width: 768px) {
// Use larger text for smaller screens:
font-size: 150%;
@media (orientation: landscape) {
// Condense text a bit because of small vertical space:
line-height: 75%;
}
}
}
Compiles into:
/* compiled CSS */
@media (max-width: 768px) {
p {
font-size: 150%;
}
}
@media (max-width: 768px) and (orientation: landscape) {
p {
line-height: 75%;
}
}
...with multiple inheritance.
TODO