Introduction
In this article I would like to introduce a few of the features of SASS (Syntactically
Awesome StyleSheets). SASS allows you to pro grammatically define several aspects
of stylesheets, "compiling" down to standard CSS. To demonstrate this
I will do this through a refactoring exercise of a pre-existing CSS file, to show
the sorts of problems SASS overcomes. Hopefully this will prove useful as a reference
as well as an introductory how to. As this is an introduction to SASS I will
just introduce the syntax, I don’t want to cover some of the finer aspects such
as configuring SASS for caching or output style etc.
Background
In general this topic is aimed at beginners at SASS, but you should know HTML
and CSS reasonably well for this article to be of any use. The following is a list
of resources you may find useful.
A Note About LESS
Many of the concepts apply to LESS, a similar CSS pre-processor language. I prefer
the syntax of the basics of LESS as it feels more "css-ish",
but for much the same reasons as
this article, I think SASS pips it to the post. Either is a good choice however.
Less can be found at http://lesscss.org/ .
Another reason I am using SASS is that is used in a website I have just started
working on. As such my exposure to SASS is pretty limited and in writing this article
I both to firm up what I have covered so far and to help promote the technology
itself. When I started to look at SASS-like CSS generation, it is one of the few
technologies that immediately screamed "this is a no-brainer to adopt",
obviously with the caveat that the project you are working on is capable of using
it.
Setting Up
The first item ticked off my to-do list was to create an MVC3 application as
the sample code. If you don’t know how MVC3 works, should not be a problem: the
generated HTML is what is important on the mark-up side. I will not reference the
MVC3 view at all, I could just as easily used an HTML document. I have included
the generated html (and the original css) in a solution folder called “Vanilla HTML
and CSS” in the sample code, so you can refer to these if you are unsure about MVC3
and Razor. Running the application gives:
Getting SASS
The easiest way is to do this via NUGET (which can be installed from
http://nuget.org/)
package, first open the package manager console from the visual studio IDE:
Then, in the console window copy/type the following command:
install-package sassandcoffee
Nuget will install SASS into your project, you will be prompted to install definition
files, select yes. You could also use the “Manage NuGet Packages for Solution” option
if you prefer a GUI, search for "sassandcoffee".
At this point it is worth installing "Mindscape Web Workbench" which
provides intellisense, formatting and other editor support for SASS. Close Visual
Studio, download Workbench from
here and open the .vsix file to install. You need only do this once for your
Visual Studio installation.
Using SASS
The first thing that we need to understand is that CSS can be used directly with
SASS, doing this is as simple as renaming the “.CSS “ file to “.SCSS”. If you installed
Workbench the icon will like this change:
Running the app produces the same output as the earlier screenshot. The scss
file is "compiled" down to a css which is used by the site.
The eagle-eyed amongst you will have noted file extension is scss rather than
sass. There is an older syntax based on indentation which uses the .sass extension.
While terse, it is not as natural to a web developer as the scss syntax: vanilla
css is not valid in a .sass file but is valid
in a .scss file. I will not cover the sass syntax as I see no compelling reason
to use it in new development work. The .scss files follow css syntax much more closely.
The really eagle-eyed amongst you will have noticed that I did not edit the reference
to the css in the html header and the page still rendered. In an ASP.NET Project,
Workbench "Compiles" the .scss file into a css when it is saved
in Visual Studio. This has the added benefit that when the application is deployed
to the server, the server does not need to do this at runtime and there is no performance
hit to speak of.
OK, we've spent a little while and are still at square one, so what is the excitement
about?
Variables
The CSS for the default MVC project is fairly well structured, however one instructive
thing is the comment bang at the top of the file:
If you want to change the base colour to, say, pink you need to do a search and
replace! SASS can handle it more elegantly: we just define a variable and use it:
$base-color:
#FFA0A0;
body
{
background-color: $base-color;
…
}
This is worth going through: The $
symbol tells SASS that there
is a variable. $base-color: #FFA0A0;
sets the variable to a rather
fetching pink. background-color: $base-color;
sets the background colour
of the to the value of the parameter value. Notice that as part of SASS
c-style single line comments are available!
The only other place the base colour is used in this instance is the in the bottom
border of the menu:
ul#menu
{
border-bottom: 1px $base-color solid;
…
}
Now we only need to change the base-color
variable to change the
whole page. I want to darken this border so it stands out more. SASS has
many built in functions one of which darkens. I will also make the border
wider to make it more visible:
ul#menu
{
border-bottom: 5px darken($base-color, 33%) solid;
…
}
Rendering produces:
We are not limited to colours. We can set a variable to many things, for example
the much of the text is based on em sizes, for example the headers, and perform
operations on them, for example setting font heights according to a base size.
h1, h2, h3, h4, h5,
h6
{
font-size: 1.5em;
…
}
h1
{
font-size: 2em;
…
}
p, ul
{
…
line-height: 1.6em;
}
etc, becomes
$base-text-size:
1em;
h1, h2, h3, h4, h5,
h6
{
font-size: $base-text-size + 0.5;
…
}
h1
{
font-size: $base-text-size + 1;
…
}
p, ul
{
…
line-height: $base-text-size + 0.6;
}
Notice that I performed an operation on the variable here, additionally I did
not need to specify the unit (in this case em), SASS worked it out for me!
How Do I Read the Generated CSS To Debug
One thing you may have spotted is the need to see what is being generated.
There are two methods, both very easy. The first (available with workbench) is to
expand the tree under the .scss file, the generated css is there. The second method
is to enter the url of the referenced stylesheet into the browsers as if SASS were
not in use. For the default MVC3 app it is
http://localhost:nnnn/Content/Site.css
where nnnn is the assigned port number, in the sample app this is set to
1999.
One really nice feature is that, if you make a syntax error workbench puts a
report into the css. Suppose I miss the terminating ;
on the base colour
parameter declaration:
$base-color: #FFA0A0
The page will render without any styling: SASS could not produce the stylesheet.
If you navigate to the css path as above, a handy error message is produced:
Creating Chunks of CSS Properties: Mixins
Mixins are like super variables, the allow you to set several properties in one
go. In the app I want to lighten the header and the footer, and give them
rounded edges. Some css/scss I could use is:
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
background-color: lighten($base-color, 10%);
The first three properties allow cross browser support via web kits while the
CSS3 specification is implemented. The final line lightens the background color
by 10% through a SASS function. I could put this into both the header and footer
css sections, but it seems to be the sort of thing we’d want to achieve often. The
first step is to abstract out to a mixin:
@mixin round-corner
{
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius;
background-color: lighten($base-color, 10%);
}
Now we include the mixin where we want it to be applied
#header
{
…
@include round-corner();
}
#footer
{
…
@include round-corner();
}
Rendering the page produces:
OK there are a few things here. We can easily foresee the re-use of the
rounded corners mix in for other elements with less or more rounded corners. In
the screenshot the footer is against a white background, I do not want it to be
so dark, and as it has no content the corners are too large. I can still keep my
single mixin by parameterising both the corner radius and the amount to lighten
by:
@mixin round-corner ($radius, $lightenAmount)
{
border-radius: $radius;
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
background-color: lighten($base-color, $lightenAmount);
}
And now we just provide the values where it is used:
#header
{
…
@include round-corner(10px, 10%);
}
#footer
{
…
@include round-corner(5px, 5%);
}
Here are the results:
The @
before the @mixin
and @import
statements
indicates that the statement is a directive. We will introduce further directives
later in this article.
Structuring CSS
Nesting
So far what we have achieved is pretty useful, in the interests of the DRY (Don’t
Repeat Yourself) alone this is good. Now we can look at re-structuring the css itself.
Let us take a look at the css for the menu system:
ul#menu
{
border-bottom: 5px darken($base-color,33%) solid;
padding: 0 0 2px;
position: relative;
margin: 0;
text-align: right;
}
ul#menu li
{
display: inline;
list-style: none;
}
ul#menu li#greeting
{
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
}
This is pretty standard stuff, but has a few shortcomings: I have selectors for
ul#menu
, ul#menu li
and <code>ul#menu li#greeting
.
Through all this, I’m repeating (at least) ul#menu
. The other problem
is that although the styles are related, this is not clearly reflected in the css
structure. I am used to this now, but when I first started out in web development
I found it unsatisfactory, and all too easy to make a hard to trace typo in the
selector. SASS allows us to nest css selectors in a .scss file, for example the
ul#menu and its child list item:
ul#menu
{
border-bottom: 5px darken($base-color,33%) solid;
padding: 0 0 2px;
position: relative;
margin: 0;
text-align: right;
li
{
display: inline;
list-style: none;
}
}
I no longer need to include the ul#menu
before the child list item.
The css for these elements generated by SASS is semantically and syntactically the
same as before, but the nesting in the .scss file ties the elements in a more obvious
way and the risk of mis-typing a compound selector is now reduced. Nesting
is not only one level deep, I can nest further elements, if I add the greeting:
ul#menu
{
border-bottom: 5px darken($base-color,33%);
padding: 0 0 2px;
position: relative;
margin: 0;
text-align: right;
li
{
display:inline;
list-style: none;
#greeting
{
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
}
}
}
Looking at the generated css highlights a gotcha with nesting. The ID #greeting
needs to be immediately after the li
but the generated css has a space:
ul#menu li #greeting
. To overcome this, we need to use the parent selector
&
before the greeting, so the above becomes
ul#menu
{
border-bottom: 5px darken($base-color,33%) solid;
padding: 0 0 2px;
position: relative;
margin: 0;
text-align: right;
li
{
display: inline;
list-style: none;
&#greeting
{
padding: 10px 20px;
font-weight: bold;
text-decoration: none;
line-height: 2.8em;
color: #fff;
}
}
}
Now the generated CSS is as expected. The parent selector is more often used
for pseudo-classes, such as a:hover
, a:link
etc
were we’d expect to see something like:
a
{
…
&:hover
{
…
}
&:link
{
…
}
}
Property Nesting
In addition to the standard nesting available above, we can nest properties,
a good example is setting font properties, as in the styling for the body:
body
{
…
font-size: 75%;
font-family: Verdana, Tahoma, Arial, "Helvetica Neue";
…
}
Nesting the properties, this becomes:
body
{
…
font:
{
size: 75%;
family: Verdana, Tahoma, Arial, "Helvetica Neue", Helvetica, Sans-Serif;
}
…
}
Breaking up the SCSS File
Nesting the css goes some way to adding some logical structure to the file, but
the file itself is long. We probably do not want multiple css files as this puts
more work the server and decreases page load /render times. SASS allows us to break
the scss file through use of the @import
directive. We can import plain
css files or .scss this way, performance a live server is unaffected as the result
is still our sinlge css file if we set up correctly. We can break scss file
down into multiple separate ones, the obvious places to refactor are where Microsoft
added a comment as a heading (e.g. for the menu to separate the menu styling in
the file). I will do this in the sample application, but it would be unwieldy
to show this in the article, so see the solution for what I did. I want to
make one further additional .scss file for common declarations which I will use
as the example here. Remember the parameters and rounded corner mixin we created
earlier in this article?
$base-color: #FFA0A0;
$base-text-size: 1em;
I also know that I am going to add further features to this file later in the
article, so I want to separate these out into a file called "CommonDeclarations.scss"
As workbench is installed this is easy, right click the "Content" folder
Visual Studio’s solution explorer and select "Add a new item". Under “Web”
(I had to scroll YMMV) you will find this
Change the name to “_CommonDeclarations.scss” and click add, the underscore
prefix by convention, telling SASS the file is for import (a Partial File). Next
I just cut the things I want in this file from the old and paste into the new. The
final step is to add the line @import "CommonDeclarations";
to the. scss files where things defined common declarations are used Note that I
do not need the file extension or the _ prefix. Now I can continue to break
down the main site.scss file in the same way. I will create a file for each comment
acting as a section header in the original css added by Microsoft when the
project was created as an example as each of these tend to deal with one logical
grouping.
Other Directives
As stated earlier, the @
prefix denotes a directive, there
are other s built in that allow us to increase our DRY creamy goodness. I will visit
these in turn
The @extend Directive
This allows us to derive on style based on another. The css that deals with the
validation elements on any web-forms is a good example. After the refactoring into
separate style sheets, this currently sits in the _ValidationHelpers.scss file:
.field-validation-error
{
color: #ff0000;
}
.validation-summary-errors
{
color: #ff0000;
font-weight:bold;
}
Both these items are in red, it is likely if we want to change the colour we
will want to change both. We could use a parameter in this basic example, but
there is also arguably a genuine relationship here, the summary style relates
to the field validation, so I will remove the colour on the summary style and extend
the field validation style:
.field-validation-error
{
color: #ff0000;
}
.validation-summary-errors
{
@extend .field-validation-error;
font-weight: bold;
}
I only need to do this if there is a true reliance of one style on another, the
litmus test of this is that if we want the base style to change, should the extended
one. One thing to note is that the css generated is slightly different now:
.field-validation-error,
.field-validation-error, .validation-summary-errors {
color: #ff0000; }
.validation-summary-errors {
font-weight: bold; }
Note that the css has been part-minified, this is something else that SASS can
provide, though we do not cover it in this article. It is possible to extend from
extended styles (called chaining) and to use multiple extends in the same block,
though none of the project css is complicated enough to warrant this. It is also
possible to extend complex selectors, such as a:hover
, the syntax is
just the same. More documentation on extend can be found
here
Other Directives – Functions and Control Flow
For completeness we will cover some of the more advanced fundamentals.
Functions
SASS Has built-in functions, for example the function hsl
takes three arguments hue, saturation and lightness and converts it to RGB:
As an example, we shall set the H1 used in the main layout to a mid-purple colour
using the hsl function.
#header h1
{
…
color: hsl(308, 44%, 28%);
…
}
This compiles to
#header h1 {
…
color: #67285e;
…
}
The documentation recommends using explicit keyword arguments, whilst meaning
the same thing as above, it suggests the following in the interests of clarity:
#header h1 {
…
color: hsl($hue: 308, $saturation: 44%, $lightness: 28%);
…
}
Whichever syntax you use, the results ins the rather fetching design
Extending the Available Functions
We can extend the list of functions to add our own. Let's suppose we want to
standardise our widths to be multiples of 10px. We could provide a function to do
this:
$base-width: 10px;
@function keith-width-multiplier($multiple)
{
@return $multiple * $base-width;
}
Note that I have prefixed the function name with keith, the SASS documentation
suggest that you prefix all custom functions with something to distinguish
them from the pre-existing ones such as hsl
. It suggests using your
company name, but as I am working in my own time I have decided to show great humility
and use my own name
.
Now, to take a hypothetical example:
.foo
{
width: keith-width-multiplier(3);
}
This would result in everything with class foo having a width of 30px.
Control Flow Directives
These are considered an advanced feature in SASS, even the documentation suggests
you should not use these normally: http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#control_directives.
I have included them as with these, the SASS syntax is functionally complete. Like
the custom function above, I have not included the examples below in the sample
code, they are simply not needed. The examples that follow may also not be the best
way to achieve what is needed (they are not included in the sample code), they are
intended to be as clear as possible rather than real-world.
@if
As we would expect , this allow a decision. Let’s suppose we want to set the
colour of a span according to a variable $backgroundcolor
. If the is
white, color
should be black, if is $backgroundcolor
black
color
should be white, otherwise color
should be gray.
span
{
@if $backgroundcolor = #FFFFFF
{
color:#000000;
}
@elseif $backgroundcolor = #000000
{
color:#FFFFFF;
}
@else
{
color:#696969;
}
}
Naturally, the above would benefit from some refactoring in a real situation!
@for and String Interpolation #{…}
This is our first looping construct, it loops between values. With this we can
even create multiple css definitions. In this example we will create styles for
headings h1 through to h4, with diminishing font-sizes:
$biggest-width: 8em;
@for $i from 1 through 4
{
h#{$i} {
font-size: $biggest-width /$i;}
}
When compiled this generates h1:
h1 {
font-size: 8em; }
h2 {
font-size: 4em; }
h3 {
font-size: 2.667em; }
h4 {
font-size: 2em; }
}
Note the term #{$i}
, this takes the value (in thise case $i
)
and outputs it directly into the css. Not only can it be used in loops,
it can be used for property names and values too. Another thing to not that for
the four heading types this loop is overkill. If you can imagine setting up several
table column or row styles, the looping construct makes more sense.
@while
While performs a similar function to the for loop. Like any while loop in a mainstream
language, the value must change in the loop so the condition terminates. We
can re-write the for loop above as :
$val : 1;
@while $val = 4 {
h#{$val} {
font-size: $biggest-width / $val<}
}
This outputs the same css as the for loop, but is less elegant in this case:
just because you can, it doesn’t mean you should
.
In a real situation, the while loop might be used for more complex looping.
@each
Each iterates over a list, the list can be anything. For simplicity I’d like
to use the example of creating a .red class style that has red text, a black
one and a green one:
@each $color in red black green <{
.#{$color&} { color: $color;}
}
This outputs:
.red {
color: red}
.black {
color: black;}
.green {
color: green; }
In this case we have assigned a variable $color
to a value
in a list of strings
When I started web-devving many years ago I was frustrated coming from a [then]
predominately c++ background. Why did I need to keep repeating the values in the
colour palette? Why couldn’t I base the colour of one style on that of another or
a palette value? Why does the css syntax not reflect the nesting of the selectors
well? All these problems are answered more-or-less by SASS:
- Parameters
- Functions and mixins
- The nesting sytax
Additionally SASS allows us to define our own functions, allowing for extension
points. It also allows us to break down our .css into logical file blocks without
compromising on performance. Finally SASS allows us add control flow to our
CSS, pretty much making it Turing complete.
SASS (or SASS-like technologies) are almost a no-brainer assuming you have an
environment that allows you to use it and that bar is pretty low. It allows you
to simplify and rationalise your css to a degree you are happy with, inducing little
to no real cost.
- 24/07/2012 Initial Version.
- 25/07/2012 Added Missing Sample solution!