Nesting, Extend, Placeholders, and Mixins

Packt Publishing

If you know HTML and CSS, then you can have all the power of Sass and Compass at your disposal. This step-by-step guide will take you through the time-saving features that makes it so much easier to create cross-browser CSS.

Styling a site with Sass and Compass

Personally, while abstract examples are fine, I find it difficult to fully understand concepts until I put them into practice. With that in mind, at this point I'm going to amend the markup in our project's index.html file, so we have some markup to flex our growing Sass and Compass muscles on.

We're going to build up a basic home page for this book. Hopefully, the basic layout and structure will be common enough to be of help. If you want to see how things turn out, you can point your browser at http://sassandcompass.com, as that's what we'll be building.

The markup for the home page is fairly simple. It consists of a header with links, navigation made up of list items, images, and content and a footer area. Pasting the home page markup here will span a few pages (and be extremely dull to read. Don't get too hung up on the specifics of the markup.

Let me be clear. The actual code, selectors used, and the finished webpage that we'll create are not important. The Sass and Compass techniques and tools we use to create them are. You can download the code from the book's page at http://packtpub.com and also from http://sassandcompass.com

Wow, what a looker! Thankfully, with Sass and Compass we're going to knock this into shape in no time at all.

Notice that in the source code there are semantically named classes in the markup. Some people dislike this practice, but I have found that it makes it easier to create more modular and maintainable code.

A full discussion on the reasoning behind using classes against styling elements themselves is a little beyond the scope of this book. However, a good book on this topic is SMACSS by Jonathan Snook (http:// smacss.com). It's not essential to adhere slavishly to the conventions he describes, but it's a great start in thinking about how to structure and write style sheets in general.

First, let's open the _normalize.scss partial file and amend the default styles for links (changing the default text-underline to a dotted bottom border) and remove the padding and margin for the ul tags.

Now I think about this, before we get knee-deep in code, a little more organization is called for.

Separating the layout from visuals

Before getting into nesting, @extend, placeholders, and mixins, it makes sense to create some partial files for organizing the styles.

Rather than create a partial Sass file for each structural area (the header, footer, and navigation), and lump all the visual styles relevant inside, we can structure the code in a slightly more abstract manner.

Then a partial file called _layout.scss. That will only contain rules pertaining to visual layout and positioning of the main page areas (the previously mentioned header, footer, aside, section, and navigation). Here are the initial styles being added into the _layout.scss partial file:

Thanks to the popularity of Sass there are now experimental features in browsers to make debugging Sass even easier. When inspecting an element with developer tools, the source file and line number is provided, making it easier to find the offending selector. For Chrome, here's a step-by-step explanation: http://benfra.in/1z1 Alternatively, if using Firefox, check out the FireSass extension: https://addons.mozilla.org/en-us/firefox/addon/ firesass-for-firebug/

Let's create another partial file called _modules.scss. This will contain all the modular pieces of code. This means, should we need to move the .testimonial section (which should be a modular section) in the source code, it's not necessary to move it from one partial file to another (which would be the case if the partial files were named according to their existing layout).

The _module.scss file will probably end up being the largest file in the Sass project, as it will contain all the aesthetics for the items on the page. However, the hope is that these modular pieces of code will be flexible enough to look good, regardless of the structural constraints defined in the _layout.scss partial file.

If working on a very large project, it may be worth splitting the modules into sub-folders. Just create a sub-folder and import the file. For example, if there was a partial called _callouts.scss in a sub-folder called modules, it could be imported using the following code:

In the previous code, a color has been set for the link tag using a variable. Then the hover and focus state have been nested within the main anchor style with a different color set. Furthermore, styles for the visited and active state have been nested with an alternate color defined.

To reference a variable, simply write the dollar sign ( $) and then the name of the variable, for example, $variable.

I've omitted the additional list items and closing tags in the previous code for the sake of brevity. From a structure point of view, each list item contains an anchor link with a <b> tag and a <span> tag within. Here's the initial Sass nested styles that have been added into the _modules.scss partial to control that section:

Notice that the rules are being nested inside the class .chapter-summary. That's because it then limits the scope of the nested styles to only apply to elements within a list item with a class of .chapter-summary. However, remember that if the styles can be re-used elsewhere, it isn't necessarily the best practice to nest them, as they may become too specific.

The nesting of the selectors in this manner partially mirrors the structure of the HTML, so it's easy to see that the styles nested within only apply to those that are similarly structured in the markup.

We're starting with the outermost element that needs to be styled (the <li class=”chapter-summary”>) and then nesting the first element within that. In this instance, it's the anchor tag. Then further styles have been nested for the hover and visited states along with the <b> and <span> tags.

I tend to add focus and active states at the end of a project, but that's a personal thing. If you're likely to forget them, by all means add them at the same time you add hover.

The nested styles are generated with a degree of specificity that limits their scope; they will only apply to elements within a <li class=”chapter-summary”>.

It's possible to nest all manner of styles within one another. Classes, IDs, pseudo classes; Sass doesn't much care. For example, let's suppose the <li> elements need to have a dotted border, except for the last one. Let's take advantage of the last-child CSS pseudo class in combination with nesting and add this section:

The parent selector

Notice that any pseudo selector that needs to be nested in Sass is prefixed with the ampersand symbol (&), then a colon (:). The ampersand symbol has a special meaning when nesting. It always references the parent selector. It's perfect for defining pseudo classes as it effectively says 'the parent plus this pseudo element'. However, there are other ways it can work too, for example, nesting a few related selectors and expressing different relationships between them:

We have effectively reversed the selectors while nesting by using the & parent selector.

Chaining selectors

It's also possible to create chained selectors using the parent selector. Consider this:

.selector-one {&.selector-two {color: green;}}

That will compile to this:

.selector-one.selector-two {color: green;}

That will only select an element that has both HTML classes, namely selector-one and selector-two.

The parent selector is perfect for pseudo selectors and at odd times its necessary to chain selectors, but beyond that I couldn't find much practical use for it, until I saw this set of slides by Sass guru Brandon Mathis http://speakerdeck.com/u/imathis/p/sass-compass-the-future-of-stylesheets-now. In this, he illustrates how to use the parent selector for nesting a Modernizr relevant rule.

Easy Modernizr styles with the parent selector

Modernizr provides the perfect situation in which to utilize the parent selector. When a fork is needed in CSS, Modernizr can save the day. If forking code sounds alien, fear not, this will make more sense shortly. This is a bit of a tangent but I think it's worthwhile.

Let's download Modernizr (http://modernizr.com) and save it into the js folder. Then add a reference to it in the section of the web page:

<script src = "js/modernizr.2.6.2.min.js"></script>

If you have no idea what Modernizr is or how to use it, I'd recommend starting with this blog post: http://benfra.in/1y2

Let's use the font called Sansation by Bernt Montag as the main body text font for the site. There's an @font-face version of the font available free from Font Squirrel (http://www.fontsquirrel.com/fonts/Sansation):

The font files have been copied into a folder called fonts inside the css folder. Here's the hierarchy of the css folder in the OS X Finder:

Make a partial file for fonts

We'll create another partial file called _fonts.scss. Inside that file will be references to any web fonts needed. Before that, let's import the partial file into the main styles.scss file:

Now, we need to set up the font files in the _fonts.scss partial file. To help matters, the downloaded Font Squirrel font includes a CSS file (stylesheet.css) with the relevant font files referenced. However, it needs amending before pasting it into the _fonts.scss partial file.

First we're going to keep each font used in a sub-folder, just to keep things tidy. Furthermore, we'll use a Compass URL helper to reference the fonts. So instead of:

src: url

In the downloaded code, we will write:

src: font-url

The Compass font-url helper is worth using as it allows us to save the font files in the fonts sub-folder of the css folder. Then, because we added a fonts_dir directory in the config.rb file, Sass and Compass know that they need to look for the fonts in that location.

Changing the font with Modernizr and the parent selector

Now, with that web font set up it's time to finally use that parent selector to fork the CSS styles with the help of Modernizr. In the _body.scss partial file, the first rule looks like this:

The font-family property has been added with the value of “SansationRegular” first, then a number of secondary fonts (using a standard font stack).

Sansation looks fine at 1em size, but if the browser needs to use one of the backup fonts (if the device doesn't support @font-face), the other fonts in the stack look a little too big. In this eventuality, it would be good if the font size could be slightly less. Modernizr can help us create this fork.

When @font-face support is missing, Modernizr adds a class of .no-font-face to the HTML. In that instance, the nested rule gets applied (thanks to it being more specific and later in the cascade) amending the font-size slightly. Our nested rule actually produces the following CSS:

Lovely! By using this nesting method along with the parent selector, all these related styles are grouped together in the Sass files. Also, thanks to Modernizr, browsers that support @font-face and those that don't get a relevant font-size.

Remember that the parent selector isn't needed for nesting inline elements (<span>, <i>, <b> and likewise), classes, or IDs. Just nest them normally.

Dangers of excessive nesting

It's possible to nest styles many levels deep. Here's an example of nesting using IDs, classes, and pseudo selectors:

However, when I see chunks of code like that, I start to get a little concerned. There is a real danger that nesting this many levels deep will create CSS rules that are too specific. For example, here is the generated CSS from that little block:

As you can see, the further into the nesting we get, the more specific the outputted CSS selector becomes. In the final rule there, we are selecting a hover state on an ID selector that will only be relevant if it's within a class of class-within, that is also a child of a class multi-nesting that is also a child of a class nesting. To cut a long story short, you should probably just use the following (not nested within anything else):

#id_within {width: 90%;&:hover {color: $color4;}}

That would output the following CSS:

#id_within {width: 90%;}#id_within:hover {color: chartreuse;}

This means that the element with an ID of id_within can be moved anywhere else in the markup and still get the styles applied. Here's a simple rule I try and abide by: only make rules as specific as they need to be, and if nesting, try not to go more than three levels deep.

Are ID selectors bad?

There's a growing sentiment in the web developer community that if possible, when choosing selectors in CSS, IDs are a bad choice. As ever in web development, I think it depends.

The genesis of the thinking stems from the fact that ID selectors are limited by nature (a page is invalid if a single ID exists more than once on any page) and therefore, it makes rules bound to ID selectors limiting (as any styles can't be re-used elsewhere). In addition, ID selectors are more specific than a class style, and that makes it difficult to override those declarations later on.

Those are valid concerns. However, there may be times where the same ID appears on every page and you are confident you won't need to override it. In that case, don't get hung up on using an ID as a styling hook. If the option for using a class name exists, take it, but don't worry about using an ID if necessary. For a little more info on the subject, you might want to take a look at the following blog post: http://benfra.in/1yq

It's also worth knowing that as long as support for IE6 isn't necessary, it's possible to use an attribute selector to style an ID that will hook the style without the massive specificity that ordinarily comes from using an ID selector. For example:

[id="id-name"] {}

Instead of using:

#id-name {}

Nesting namespaces

Sass also lets you nest namespaced CSS properties. That's any CSS property that has a namespace reserved. For example, border has border-left, border-right, border-top, and border-bottom. Font has font-weight, font-family, and font-size. Here's an example of nesting the namespaced border property:

With Sass, it's possible to define the namespaced part of the property and then nest the possible variants within it. The same thing could be done with the margin and padding property (where there are variants such as margin-left and margin right). Personally, I find it easier to rely on the standard CSS shorthand syntax in those instances.

For the sake of clarity, using the standard CSS shorthand syntax for a margin, to remove the margin on all sides of an element except the bottom, write:

.margin {margin: 0 0 1em 0;}

When writing this CSS shorthand (which is applicable to the padding property too) remember that the values defined left to right in the code, relate clockwise starting at the top (for example, first top, then right, then bottom, and finally left). Also, be aware that when a value is zero, there is no need to declare a unit of measure (such as em or px).

When writing Sass, nesting namespaced properties isn't something I do very often. I prefer the clarity of having the full property listed. However, it's a preference thing, so be aware that nesting can be used in this manner.

While we have acknowledged that nesting should be used considerately, it is incredibly handy for defining modular sections of code. For example, here are some basic nested styles to make a module for the <aside class=”testimonial”> area:

Notice here that at the bottom of the module we have added a style for the <blockquote> tag if it also has a class of small added. This is so that if the testimonial needed to be moved into an area with constricted space such as a sidebar the font size isn't too big. For example:

It may be preferable to add an entirely different class to cater for these different visual permutations. So instead of a .blockquote. small class (that will get applied when both blockquote and small HTML classes are present on the HTML element), we might use .blockquote--small and add that HTML class to the element to denote the small variant of the original block instead.

We've also defined colors as hex values here. When using Sass and Compass there is little reason to do that.

Finally, notice how a variable has been used for the font-family of the blockquote. The variable itself is just a font-stack. Here's that variable in the _variables.scss partial file:

Using the @extend directive to extend existing rules

The @extend directive is used to extend another style. It allows any style to inherit the properties and values defined in another. Suppose there are a few elements to style that share some characteristics; they are a prime candidate for the @extend directive. Let's try an abstract example. We need to create a few boxes. A standard box, a success box, an information box, and finally a warning box. Consider this code:

Sass is smart enough to group the shared styles under a single combined selector. Using the @extend directive in this manner prevents endless repetition of code for similar elements.

Now, notice in this example, our first rule for .box class wouldn't actually be used. It's being declared for the sole purpose of extending. That may seem a little wasteful and, thankfully, the smart people working on Sass have already got a solution. In situations where we want to define rules purely to extend them elsewhere, we can use placeholder selectors. Let's look at how they work.

Using placeholder selectors to extend styles only when needed

We've just looked at how the @extend directive can extend an existing rule. However, in situations when a rule is being created purely to extend it, use a placeholder selector instead. Here's our prior example written with a placeholder selector on the first rule instead:

Instead of the normal selector (a period for a class or hash/pound for an ID selector), use a percentage symbol (%) instead. Then use the @extend directive to extend it. Here is the code that compiles to:

By using the placeholder selector instead, it prevents surplus rules being generated unless they have been extended elsewhere. We'll use placeholder and @extend selectors frequently to build the styles for the homepage of http://sassandcompass.com as they are particularly handy when working with mixins.

What mixins are and how we can use them to easily produce oft-needed code

Nesting @extend and placeholder selectors are very convenient but they don't actually produce extra code. Mixins on the other hand do. Those readers who have been unfortunate enough to use Microsoft Word or Excel may have used macros. If so, think of mixins like macros.

Typically, a mixin is defined once, then included elsewhere in the Sass file and passed any optional arguments. Then, on compilation, it generates the relevant code.

Compass has literally hundreds of ready-made mixins that address every conceivable CSS need. Don't want to dabble in writing mixins? Just use the ones that Compass provides; enjoy cross-browser compatibility that makes you feel heroic and thank the Compass team with a donation to the Compass's charityware cause: http://umdf.org/compass

However, for the curious, let's look at making a mixin and we'll soon get the hang of things.

Remember back when we added some content into our _layout.scss file, the first rule was this:

The star selector was used to select all elements and change the box-sizing model to border-box. To get the best browser support possible, the property was repeated a number of times with vendor prefixes (one for WebKit and another for Mozilla with the final official property and value pair listed last so that it supersedes the others, if present).

In case you aren't aware of what the different box-sizing models do, the border-box model allows a width to be defined for an element and it takes into account borders, padding, and content. The size set is the size that gets painted to the screen. The alternative (and at this point more commonly used) content-box box-sizing model requires content, padding, and border to be factored-in when specifying the width/ height of an element.

Microsoft actually implemented border-box sizing way back in Internet Explorer 8, so browser support is good. As I'm not concerned about IE7 and below, I'm happy to use border-box here. To read more on the matter, head over to this excellent post by all-round web genius, Paul Irish, on the matter: http://paulirish.com/2012/boxsizing- border-box-ftw/

Let's write a little mixin here to generate this code for us. Let's create another partial file called _mixins.scss and import that into our main styles.scss. Here's what the import section looks like now:

Notice, the mixins partial file has been imported after the variables partial, so any of the variables can be used in the mixins and then in turn any of the mixins can be used in the other files that come after. In the _mixins.scss partial file, we're adding this:

So how did that actually happen? Let's go back and consider our mixin code and establish the basic syntax.

Basic mixin syntax

First of all, we declare the @mixin directive and name the mixin. In this instance, it's named bs (terser than using box-shadow), but it could have been called bananas (go ahead, I don't judge):

@mixin bs($bs-type) {

Then, immediately after the name, there is an opening parenthesis and what looks like a variable placed inside before a closing parenthesis.

The variable name here is acting as an argument. Think of arguments as optional parameters or settings that can be passed when including a mixin. In this instance, it will typically be either content-box or border-box.

After the opening curly brace is the code the mixin will actually generate, followed by the closing curly brace. Here's the complete mixin again:

By placing the identical variable used as an argument at various places in the generated code, the mixin knows where to place the value.

I'm concerned that may sound more complex than it actually is, so let's do something silly to illustrate. Let's change the value of the argument being passed in the _layout.scss file to one-that-works:

Whatever is passed as an argument to the mixin when it's included gets output into the generated code on compile.

Sass will check for syntax errors on compile (missing semi-colons and the like) and display a suitable error indicating where the error is. While it can syntax check the code, it can't sanity check it. Sass doesn't know that one-that-works isn't a valid value for the boxsizing property so it will happily generate that for you in the CSS.

How to write mixins with defaults

Mixins can also be set with a default value. This means that you can include them without passing an argument and they will generate with a default value. Here's how:

If a mixin is needed that will be on lots of future projects, it might be preferable to set the mixin up so that it takes a global variable as the default value for an argument. That way, the default value will only be used if a value isn't specified in a global variable elsewhere.

First, the variable $defined-bs-type has been assigned the value of border-box. On the next line, the same variable has been set a value of sausages, with the !default flag before the closing semi-colon. The rest of the mixin is as before. Then the mixin is being included in the _layouts.scss file like this:

* {@include bs;}

Ordinarily, it might be expected that the value generated on compile would be sausages, as that is the last value assigned to the variable. However, the !default flag is telling Sass to only use this value if a different value hasn't been assigned elsewhere. Because a value has been assigned elsewhere (in the line above), it doesn't use the default. Instead, here's what is generated:

The benefit of this approach is that when you have a few mixins built they can be carried from project to project. Then the value that needs assigning can be defined as a variable elsewhere (such as a _variables.scss partial file). Then, altering the value of that variable will affect every instance of the mixin.

Compass has heaps of great mixins of its own that can be used in this manner.

Compass, purveyor of the finest mixins

Want to see just how funky mixins can get? Head over to the Compass project page on GitHub:https://github.com/chriseppstein/compass.Many of the mixins in Compass often include additional geekery such as functions and variable arguments.

A cautionary note about generated CSS

writing bad Sass will generate bad CSS. Overuse of nesting, @extend, and mixins can lead to bloated code and over-specific selectors. Therefore, apply the same level of common sense when writing Sass as you would when writing plain vanilla CSS (when I say vanilla CSS, I just mean normal CSS with no preprocessor involved in its creation). For example, only make rules as specific as they need to be, don't nest rules too deeply and don't repeat mixins unless necessary.

Getting the hang of writing Sass well from the outset is important. Using Sass and Compass to create CSS files means you will be less inclined to look at the final outputted CSS.

However, even if Sass and Compass don't generate CSS exactly as you would have written it by hand, remember that it is only the browser that actually consumes the compiled CSS (and at that point it should be compressed and illegible to humans anyway). Therefore as no human will actually need to read the generated CSS, it doesn't concern me one bit. As long as the CSS is as efficient for the browser as it can be and my Sass files make sense to others and myself, I'm happy.

CSS file size is a micro optimization

Take a look at the following URL at HTTP Archive. It shows the different types of file that make up a typical webpage:http://httparchive.org/interesting.php#bytesperpage. As you can see, CSS is the smallest content type. Therefore, when tuning CSS for performance, be aware that there are probably far better uses of time and effort in creating a faster webpage. To exemplify; optimizing a single image may well trump any optimizations you make to the entire CSS of a page.

Summary

We've covered a lot of ground in this article:

How and when to use nesting

How we can use nesting with the parent selector to work more easily with Modernizr

However, the skills we've learnt are so useful in everything we're going to write in future; I think that's a fair trade-off.

It might take some time before you find yourself using these features day-to-day. Don't feel like you need to use them right away and at every available opportunity. If all you do for starters is nest and extend the odd style that's still progress, and it will make authoring style sheets easier than before. Once comfortable with that, when feeling feisty in the months to come (and if Compass doesn't already have a feature to solve the problem), maybe take a shot at writing a mixin or two.

Alerts & Offers

Series & Level

We understand your time is important. Uniquely amongst the major publishers, we seek to develop and publish the broadest range of learning and information products on each technology. Every Packt product delivers a specific learning pathway, broadly defined by the Series type. This structured approach enables you to select the pathway which best suits your knowledge level, learning style and task objectives.

Learning

As a new user, these step-by-step tutorial guides will give you all the practical skills necessary to become competent and efficient.

Beginner's Guide

Friendly, informal tutorials that provide a practical introduction using examples, activities, and challenges.

Essentials

Fast paced, concentrated introductions showing the quickest way to put the tool to work in the real world.

Cookbook

A collection of practical self-contained recipes that all users of the technology will find useful for building more powerful and reliable systems.

Blueprints

Guides you through the most common types of project you'll encounter, giving you end-to-end guidance on how to build your specific solution quickly and reliably.

Mastering

Take your skills to the next level with advanced tutorials that will give you confidence to master the tool's most powerful features.

Starting

Accessible to readers adopting the topic, these titles get you into the tool or technology so that you can become an effective user.

Progressing

Building on core skills you already have, these titles share solutions and expertise so you become a highly productive power user.