UX Design and Development course

From indentation syntax (whitespace) Sass to Sassy CSS (SCSS)

In May 2006 Hampton Catlin introduced us to HAML. A lightweight markup preprocessor language that uses indentation (whitespace) to separate blocks. HAML replaces the more common HTML syntax <tag> ... </tag> with a simpler, more lightweight syntax %tag. HAML's true strength lies within it's use of indentation to determine selector nesting and block separation.

In this section I will discuss the differences between HTML and HAML and how these ideas inspired Sass.

From HTML to HAML

HTML relies on open and closing tags <tag> ... </tag> to separate code blocks. Placement of HTML selectors within a pair of tags designates nesting.

In the following example you will see that all the content for this block is nested within the <article> ... </article> tags. The nested <p> ... </p> tags contain the nested <a> ... </a> tags stating that the <a> inline-block element will be inside the <p> block element.

<article>
  <h1>primary header title for paragraph below</h1>
  <p>
    <a href="#">Example of body text link</a>
  </p>
</article>

It's interesting to note, that while returns and tabbing in HTML is a good form of writing clean code and does make the code easier to read, it is not necessary for nesting or block separation. The clear advantage here is with HTML minification of course.

It isn't until we begin to append styles, actions and other attributes to the markup, that reading becomes increasingly difficult. Notice in the following example by simply adding a few additional attributes to the HTML that it becomes more difficult to visually parse out the content from the functionality.

<article class="primary-article" id="main-author" data-author-id="999">
  <h1>primary header title for paragraph below</h1>
  <p>
    <a href="#" class="external-link">Example of body text link</a>
  </p>
</article>

The worst part is, this is still static HTML. Things only get worse as we begin to add logic and insert dynamic content.

Hampton invents HAML

In the previous section we saw when traditional HTML increases with complexity, it decreases in readability. It was from this perspective that Hampton came up with the four principals to good programming with HAML.

  • Markup should be beautiful
  • Markup should be DRY (Don't Repeat Yourself)
  • Markup should be well-indented
  • XHTML structure should be clear

Where HTML requires open and closing tags, HAML does not. Where HTML does not require indentation, HAML enforces it. The beauty of HAML is in it's lack of repetition, aka Do Not Repeat Yourself (DRY). HAML removes the angle brackets (chevrons) < ... > and replaces them with a single % symbol. To reduce repetition, HAML also removes of the need for a closing tag. Notice in the following example that only an opening tag with a % symbol is required to open a new HTML block.

%article
  ... content ...

Earlier I stated that HTML requires nested tags to be physically nested within each other in the markup. Following the principals of Markup should be well-indented and XHTML structure should be clear, HAML takes a more specific approach. Each return signifies a new block and a return with a two-space indent signifies a nested tag.

In the following example you will see how the <article> ... </article> tags are simply replaced with %article. You should also notice how I used indentation to nest the %h1 and %p tags within %article. Last, see how I nested the %a tag within the %p tag.

%article
  %h1 primary header title for paragraph below
  %p
    %a{:href => "#"} Example of body text link

HAML doesn't stop there. In the previous section I showed how simple HTML can get harder to read by adding attributes to the markup. HAML helps this by removing statements like class="..." and id="...".

The following example illustrates how HAML simply refers to an id by using the pound # symbol and a class is simply referred to with a period . symbol. Using this method I can take a HTML statement like <article class="primary-article" id="main-author" ... and reduce it to something as simple as %article#main-author.primary-article.

%article#main-author.primary-article
  %h1 primary header title for paragraph below
  %p
    %a{:href => "#"} Example of body text link

HTML href tags aren't exactly reduced in HAML, but the syntax uses a Ruby key:value pair style. Notice in the example how I replaced <a href="#"></a> with %a{:href => "#"}.

HAML was a radical new way of looking at HTML. HAML embraces standardized HTML and makes it easier to write, and cleaner to read.

It was from the success of these powerful concepts that the idea of Sass was born.

Good 'ol CSS

When Hampton Catlin first approached Nathan Weizenbaum, his idea was for a HAML for CSS. Much like HTML, standard CSS is full of repetition and the syntax relies on characters like semi-colons ; to separate declarations and curly-brackets { ... } to separate blocks of style rules.

The following example illustrates a common CSS selector with CSS rules. However, unlike HTML, nesting requires duplication of the parent selector(s).

header {
  width: 100%;
  height: 100px;
}

header .nav {             /* header is repeated */
  text-decoration: none;
  background: #fff;
  color: #333;
  border-radius: 5px 5px 0 0;
}

header .nav p {           /* header and .nav are repeated */
  font-weight: bold;
}

I don't know about you, but this repetition was something that always bothered me. Years before I ever encountered Sass I would beg the question, "Why can't I just return+tab?" As it turns out, others were thinking the same thing.

It should be noted, in the same way returns and indentation are best practice for formatting HTML, the same goes for block and declaration separation in CSS.

In the following example I illustrate three styles of CSS, expanded, nested and compact.

/* Expanded */
header {
width: 100%;
  height: 100px;
}
header p {
  font-size: 48px;
}

/* Nested */
header {
  width: 100%;
  height: 100px; }
  header p {
    font-size: 48px; }

/* Compact */
header { width: 100%; height: 100px; }
header p { font-size: 48px; }

Since CSS formatting relies on syntax for block and rule separation, CSS authors are able to use all three styles at the same time in the same document if desired.

In many cases there is a distinct reason why multiple styles are used in a single document. There are also a lot of cases where the code was simply edited by multiple developers who preferred one style over another. Going unchecked, this lack of style enforcement can quickly lead to chaos in the code and makes it almost impossible to maintain. Remember the campfire rule?

Another format style of CSS is compressed. This is typically used for file compression and not a writing style.

HAML for CSS == Sass

Like it's older brother HAML, Sass lacks the dependency on special characters, curly-brackets { ... } and semi-colons ;, that CSS does. Which means, Sass requires whitespace, returns to designate block and rule separation, and indentation for nesting.

The following example illustrates a CSS declaration that is associated to the parent selector by a return and then a two-space tab. Each following declaration is separated by a return. The level of indentation maintains the relationship between parent and siblings.

Notice that the nested .nav and p selectors are not duplicated but are using a return and then a two-space tab. Sass understands these relationship between selectors. The ability to nest without duplication, Sass' most fundamental feature, is clearly inherited from one of HAML's core principals.

header
  width: 100%
  height: 100px
  .nav
    text-decoration: none
    background: #fff
    color: #333
    border-radius: 5px 5px 0 0
    p
      font-weight: bold

It was this un-CSS looking CSS that made Sass a popular choice with early adopters using HAML. Many developers, especially those in the Rails community, were quickly replacing HTML with HAML and Sass was a clear choice as a replacement for CSS.

Some would argue, it was this same un-CSS looking CSS that kept many new users from using Sass. This new way of writing CSS, this new syntax, meant that your current code had to be either converted or trashed. To new users, not familiar with how CSS preprocessors worked, it was one thing to use Sass and it was another thing entirely to convert a project over.

Other CSS pre-processors force the hand

As Sass' popularity began to grow, competing CSS preprocessors began to emerge. A trend that clearly showed that not only Rails developers were interested in these ideas.

New languages, specifically LESS and xCSS, showed up on the scene and share many, but not all, of the same features of Sass. What made them stand out from Sass was their adoption of the standard CSS syntax. Developers using languages such as PHP or .NET were not yet used to the idea of indentation languages like HAML and Sass. Adding to this, UI developers who's bread-n-butter is CSS, were not to excited about Sass' syntactical approach either.

Sass found itself at the crossroads. CSS3 was gaining in strength and popularity. New features were being added to the spec at lightening speed. Not to mention, code examples that could quickly be executed in other competing languages required conversion in order to be used with Sass. Sure there was a quick command line tool to convert CSS to Sass, but those who were bothered by the syntax typically were not fans of the command line either. Use of the standard CSS syntax by competing languages made them more desirable to a new audience of preprocessor users. Frankly, Sass' original syntax was being viewed by some as a barrier to the language.

March 31, 2010, Nathan Weizenbaum announced Sass 3 beta release the new CSS-superset syntax to be known as "SCSS" or "Sassy CSS". It was becoming clear that Sass had outgrown it's humble beginnings as simply an aesthetic alternative to CSS. Sass was becoming a language all of it's own. A language that has it's own path and requires an approach that is not exclusively tied to HAML. This announcement, and the work that followed it, was in direct response to the changing world. Whereas HAML and the whitespace writing style was leveraged for Sass adoption in the beginning, SCSS was to intended to bring in a much larger community of CSS developers that Sass was eager to have.

The addition of the SCSS syntax was a major undertaking by the core team and one that was fully embraced. Even to the extent that the Sass reference docs were rewritten to use the new CSS extension syntax. A brief summary of the work done is as follows:

  • SCSS was built from the ground up based on the CSS3 spec, and is 100% CSS3-compatible
  • SCSS can do anything Sass can do
  • SCSS files can import Sass files, and vice versa

It is important to note that the inclusion of the SCSS syntax did not mean the deprecation of the original whitespace syntax. Nathan Weizenbaum recognizes the indented sass syntax is here to stay that a large part of Sass' initial success was due in part to people who do not prefer the standard CSS syntax and he was not about to alienate a large portion of it's users.

Be it the original Sass syntax or the newer SCSS syntax, the good news is that you don't really have to choose. Positions, personalities and preferences will vary greatly as to how to write Sass. Maybe you choose to only write Sass, or maybe you write only SCSS, or maybe you do a little bit of both? Due to the complete compatibility of the two syntax, .sass files can live harmoniously with .scss files in the same project.

I should note, the Sass whitespace syntax and the SCSS syntax cannot live in the same file. That is it's only limitation.

Sass compared to SCSS

Sass is a language and a syntax. SCSS is a syntax of Sass. Both syntax support the same features equally. Throughout the book, I will interchangeably refer to Sass as the language and the syntax. SCSS will only refer to the syntax of the language.

The easiest way to identify Sass, of course, is the lack of semicolons ; to separate declarations, or curly brackets { ... } to separate selector rules or blocks as shown in this example.

.block  // no opening curly-bracket
  font-size: 1em  // no semi-colons
  color: $text-color
  border: 1px solid $border-color
  // no closing curly-bracket

Advantage: Less characters to write. Style of writing that enforces code standards. Disadvantage: Any conventional CSS needs to be converted before it can be used. Using Sass meant learning a new style of writing for CSS.

SCSS, on the other hand, brings back the more familiar CSS appearance with the use of semicolons ; to separate declarations and curly brackets { ... } to separate rules as shown in the following example.

.block {              // opening curly-bracket
  font-size: 1em;     // semi-colons separating declarations
  color: $text-color;
  border: 1px solid $border-color;
}                     // closing curly-bracket

Advantage: No conversion required. Any correctly formatted CSS can be imported. Converting whole sites to SCSS simply means updating .css to .scss . If you are already comfortable writing CSS, you are good to go with SCSS. Disadvantage: Curly brackets { ... } and semicolons ; are back. CSS selectors are required to be more expressive.

I should also mention that all valid forms of writing CSS is now acceptable in writing SCSS. Illustrated in the following example, you will see the familiar CSS writing styles of expanded, nested and compact. Without a doubt, we will leave compressed for magnification and not illustrate that as an acceptable writing style.

// Expanded
header {
  width: $header-width;
  height: $header-height;
}

// Nested
header .nav {
  text-decoration: none;
  background: $background-color;
  color: $text-color;
  border-radius: $default-radius $default-radius 0 0; }

// Compact
header .nav p { font-weight: bold; }

SCSS' ability to combine different styles of writing is embraced by many of Sass' power users as illustrated in this sample CodePen example by John Long where he is creating a mixin using the @content directive.

@mixin keyframes($name) {
  @-webkit-keyframes $name { @content }
  @-moz-keyframes $name { @content }
  @-o-keyframes $name { @content }
  @keyframes $name { @content }
}

Sass, on the other hand, requires this code to be written in the following way, illustrating the necessity of whitespace.

=keyframes($name)
  @-webkit-keyframes #{$name}
  @content
  @-moz-keyframes #{$name}
    @content
  @-o-keyframes #{$name}
    @content
  @keyframes #{$name}
    @content

While SCSS supports a combination of writing styles that authors have come to appreciate, Sass' streamlined code writing style has a few shortcuts that are desired by many SCSS authors. A common feature, mixins, only requires a + symbol followed by the name of the mixin and the optional argument(s) as shown in this example.

block
  +buttons($button-color)

Whereas SCSS requires you to be more expressive, the full @include statement is required as shown.

block {
  @include buttons($button-color);
}

Sass' shorthand style of writing also carries forward when creating a new mixin. Notice the new mixin buttons is created by only using the = symbol.

=buttons($color)
  border-radius(3px)
  bacground-color: $color
  line-height: 1.5em

Whereas again, SCSS' expressiveness requires the full @mixin directive to be clearly stated.

@mixin buttons($color) {
  border-radius: 3px;
  bacground-color: $color;
  line-height: 1.5em;
}

Other directives like @extend and @function currently do not have a shorthand versions in either the Sass or SCSS syntax.

Probably one of the more interesting uses of SCSS is to import vendor styles written in CSS. As a matter of convention, I will create a folder in my Sass directory called vendor and include all my vendor specific CSS. By simply updating the file type from .css to .scss, these styles are now part of my Sass architecture.

Currently there is a debate about the ability to directly import a CSS file. While this can be accomplished within a Rails project via the asset pipeline, this is not a feature that Sass directly supports. I feel that there is some logic to this decision. It is definitely considered best practice that once you start using Sass that all your files should be Sass and that having a mix of Sass and CSS can only create more problems then it solves.

One of the luxuries of working with a preprocessor is that at any time you can toss the temporarily processed CSS files and re-process your Sass. But if you have a mix of Sass and CSS files in your project, this increases maintenance complexity. I don't like things to be unnecessarily complex, so 100% of my processed CSS comes from either Sass or SCSS files.

This is not to say that this solution is for everyone. If you are a user that requires additional CSS resources to be included as native CSS, Chris Eppstein released a Gem that will do this for you. The Sass CSS Importer Plugin, as Chris describes it, "The Sass CSS Importer allows you to import a CSS file into Sass."

In this section I took us on a short journey from the days when Sass was just a gleam in Hampton's and Nathan's eye all the way to present day when Sass has matured into a fully featured language that supports to distinctly different writing styles. Each style with impassioned users on either side. In the end, one is not better then the other and these different syntax types were created, and continually maintained, to serve different purposes.

It is up to you, the user, to decide what is best for you. Myself, I have taken the journey 360. I stated with the original Sass syntax, fully converted over to SCSS and now find myself easily writing both. Use each syntax to their strengths. Don't be overly dogmatic for one over the other, I hear these silly arguments all the time. There is a lot to be gained by simply understanding why someone else may prefer one style over the other.

Now that we know where Sass came from, in the next section I will discuss best practices for how to write clean Sass that will keep you from being a pain in the Sass to your team.