Wednesday 24 February 2016

Sass: The @mixin Directive - Vanseo Design

Sass: The @mixin Directive - Vanseo Design


Sass: The @mixin Directive

Posted: 23 Feb 2016 05:30 AM PST

Being able to reuse code across a project has benefits in maintenance and development efficiency. So far in this series, I've talked about the @import and @extend directives and both help to make your code more reusable. Sass also offers the @mixin directive as a means for reusing code.

Audio Mixing Board

In last week's post, I talked about the @extend directive and how it helps you write DRYer CSS by allowing one selector to inherit styes from another selector. It's not a perfect solution as it isn't particularly flexible and it can tie selectors together in ways you didn't intend.

The @mixin directive is another way to reuse styles across your stylesheet. Mixins can contain anything that is allowed in CSS or Sass and they're more powerful than @extend because you can pass arguments to mixins that allow you to modify the styles when you use them.

Defining Mixins

You define a mixin using the @mixin directive followed by the name of the mixin. Inside the curly braces you define whatever styles you want to reuse.

1  2  3  4  5  6  
@mixin button {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }

For historical reasons both hyphens and underscores are considered to be the same. That means @mixin button-large { } and @mixin button_large { } are considered the same mixin.

Again mixins can contain anything that's valid in CSS or Sass such as other selectors and parent references.

1  2  3  4  5  6  7  8  9  
@mixin link {     a {      color: blue;      &:visited {color: purple;}      &:hover {color: white;}      &:active {color: red;}     }  }

Using @mixin Directives

To use a mixin you use an @include directive, which does what the name says and includes the mixin being referred to.

1  2  3  4  
.button-green {     @include button;     background-color: green;    }

The @include lists the name of the @mixin being called. Assuming the @include refers to the button mixin I created previously, this code compiles to:

1  2  3  4  5  6  7  
.button-green {     font-size: 1em;     padding: 0.5em 1.0em;     text-decoration: none;     color: #fff;     background-color: green;    }

As you probably expected the @include was replaced with the styles that were inside the button mixin. The mixin styles were followed by the background-color that I added to the .button-green class.

Mixin definitions can include other mixins.

1  2  3  4  
@mixin button-blue {     @include button;     @include link;    }

In this way you can build up complex mixins by including a variety of simple @mixin directives.

When mixins contain selectors and rulesets that by themselves are valid CSS those mixins can be included outside of another ruleset. Here's the link mixin from earlier in this post.

1  2  3  4  5  6  7  8  9  
@mixin link {     a {      color: blue;      &:visited {color: purple;}      &:hover {color: white;}      &:active {color: red;}     }  }

The following call to the link mixin isn't inside of a selector. It appears at the root of the stylesheet.

1  
@include link;

The code will compile to:

1  2  3  4  5  6  7  
a {      color: blue;      &:visited {color: purple;}      &:hover {color: white;}      &:active {color: red;}    }

This works because the mixin contained both a a selector and styles inside. Had the selector not been present nothing would be compiled. For example if we try to include the button mixin outside of a selector there will be nothing to compile

1  2  3  4  5  6  7  8  
@mixin button {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    @include button;

This doesn't compile because where would you place the properties and values? What would they be styling.

Arguments in Mixins

Mixins can accept and use arguments and this makes them much more powerful than using @extend. Here I updated the button mixin from earlier to include a background color that gets passed to the mixin as an argument.

1  2  3  4  5  6  7  
@mixin button($background) {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;      background: $background;    }

Notice that the argument is set as a variable and then used as the value of the background property. To create a green button we can now do the following.

1  2  3  
.button-green {      @include button(green);    }

When the Sass is compiled the value green is passed to the @mixin and it becomes the value of the variable $background.

1  2  3  4  5  6  7  
.button-green {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;      background: green;    }

You can pass multiple arguments to a mixin by separating them with commas in both the @mixin and the @include.

1  2  3  4  5  6  7  8  9  10  11  
@mixin button($background, $color) {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: $color;      background: $background;    }    .button-green {      @include button(green, #fff);    }

Default Values

You might be wondering what happens if you define a mixin with arguments, but the values for those arguments aren't passed to the mixin through the @include. You'll receive a compile error, which I assume you don't want. To ensure no error is thrown you can supply default values when you define the mixin.

1  2  3  4  5  6  7  
@mixin button($background: green) {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;      background: $background;    }

Now if you forget to pass a value to the argument.

1  2  3  
.button-green {      @include button;    }

Your code will compile using the default color you assigned to the argument, in this case green.

1  2  3  4  5  6  7  
.button-green {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;      background: green;    }

You can still override the default by supplying a value.

1  2  3  
.button-blue {      @include button(blue);    }

And the code will compile using the values you provided.

1  2  3  4  5  6  7  
.button-blue {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;      background: blue;    }

Keyword Arguments

To help make your code more understandable you can include the argument name along with the value when you pass the value to the mixin.

1  2  3  
.button-green {      @include button($background: green, $color: #fff);    }

Keyword arguments add some excess code, but they can make your @include easier to understand. For example the code above is easier to understand than the code below because it's clearer what the green and white colors are for.

1  2  3  
.button-green {      @include button(green, #fff);    }

Granted the name of the selector gives us a clue to what the first value is for, but it doesn't help much with the second and a year later you may not remember.

Passing both keyword name and value together is also referred to as named arguments and you can pass named arguments in any order. Either of the following will produce the same compiled code.

1  2  3  4  5  6  7  
.button-green {      @include button($background: green, $color: #fff);    }    .button-green {      @include button($color: #fff, $background: green);    }

Because named arguments are variable names, underscores and dashes can be used interchangeably for historical reasons.

Variable Arguments

Mixins can take an unknown number of arguments. For example you can add multiple box-shadows to an element. Here I added one dark gray shadow and one light gray shadow.

1  2  3  4  
.container {      box-shadow: 0px 1px 2px #333,                  2px 3px 4px #ccc;    }

On another element you might only want a single shadow and perhaps you'll want to add a third and fourth shadow to yet another element.

You can create a mixin that accepts a variable number of arguments and you can decide how many when you pass values through the @include.

1  2  3  4  5  6  7  
@mixin box-shadows($shadow...) {      box-shadow: $shadow;    }    .container {      @include box-shadows(0px 1px 2px #333, 2px 3px 4px #ccc);    }

You allow the mixin to take a variable number of arguments by adding the three dots (…) at the end of the variable name. Note that these are three period characters and not a single ellipses character. When you pass values through the @include, you separate each with a comma.

The previous Sass will compile to:

1  2  3  4  
.container {      box-shadow: 0px 1px 2px #333,                  2px 3px 4px #ccc;    }

Behind the scenes Sass packages all the arguments into a list, but as I haven't yet covered Sass lists in this series, I'll hold off providing any more details.

You can also pass variable arguments to a mixin.

1  2  3  4  5  6  7  8  9  
@mixin box-shadows($shadow...) {      box-shadow: $shadow;    }    $shadows: 0px 1px 2px #333, 2px 3px 4px #ccc;    .container {      @include box-shadows($shadows...);    }

Here I set the variable $shadows to a list of values for two box shadows, which are then passed through the include as a variable argument. You can do the same by setting the variable to a map instead of a list. You can also pass both a list and a map as long as the list comes first.

1  
@include box-shadows($list..., $map...

Again I'll hold off showing details using lists and maps as I haven't talked about either previously. I'll talk more about both lists and maps in a future series. I mention them here in case you're already familiar with one or both.

Closing Thoughts

Mixins are a powerful Sass directive for reusing styles. The ability to pass arguments to them is what makes them so powerful.

You can create mixins with default values to prevent errors and you can override those defaults when importing the mixin. You can even pass variable amounts of arguments for greater control and flexibility.

There's still more to cover with the @mixin directive. Next week I want to talk about how you can pass complete blocks of content to mixins before considering the question of when you might want to use @extend and when you might want to use @mixin.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now

Wednesday 17 February 2016

Sass: Placeholders and @extend-Only Selectors - Vanseo Design

Sass: Placeholders and @extend-Only Selectors - Vanseo Design


Sass: Placeholders and @extend-Only Selectors

Posted: 16 Feb 2016 05:30 AM PST

Sass' @extend directive allows one selector to inherit the styles of another and it does so in a way that produces DRY CSS code. When combined with placeholders you can further abstract your code so that some styles are compiled only when extended and used in another selector.

A placeholder for a newspaper image

Last week I walked you through the @extend directive and how it's used. Today I want to continue talking about @extend, particularly in combination with placeholders. I'll close with a few odds and ends that didn't find their way into last week's post.

Placeholders and @extend-Only Selectors

Imagine you have a handful of buttons to style. The buttons all share the same characteristics, except that each will be a different color.

You create a .button class to contain all the code common to your buttons and then you create additional classes for each to extend the .button class before adding a background color.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  
.button {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-red {      @extend .button;      background: #900;    }    .button-green {      @extend .button;      background: #090;    }    .button-blue {      @extend .button;      background: #009;    }

The Sass compiles to:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  
.button, .button-red, .button-green, .button-blue {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-red {      background: #900;    }    .button-green {      background: #090;    }    .button-blue {      background: #009;    }

Since every one of your buttons likes requires a background color, you'll probably never use the .button class directly. Instead you add one of the .button-color classes to your HTML.

That's where placeholders come in. They can be used anywhere a class or id can and they use a percent sign (%) instead of a hash (#) or a period (.). The difference is they aren't compiled to CSS.

Here's the previous example with one small change. Instead of extending a .button class, I'm extending a %button placeholder.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  
%button {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-red {      @extend %button;      background: #900;    }    .button-green {      @extend %button;      background: #090;    }    .button-blue {      @extend %button;      background: #009;    }

Here's the code after it's been compiled. There's no selector for .button (or %button) as the placeholder isn't included when the code is compiled to CSS.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  
.button-red, .button-green, .button-blue {      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-red {      background: #900;    }    .button-green {      background: #090;    }    .button-blue {      background: #009;    }

That might not seem like a big savings in code. Here it only saved one instance of the text .button. However, it's useful if you're developing a theme or library, where you might create dozens of buttons that will only have their code output if they're being called in the theme.

You might also have .button-green and .button-blue inheriting button styles from several different selectors. Maybe one for buttons and another for background colors. You could conceivably have all sorts of selectors that only exist to get inherited by other classes, but never used directly.

Silent Classes

Placeholders act like silent classes in that they allow you to use @extend to inherit styles, but those styles won't compile to CSS unless they're extended and used by another selector.

By default Sass will add the extending selector alongside the selector being extended, separating both with a comma. For example here a style is applied to the .button class and an additional style is applied to any .button located in the .sidebar. A .button–2 class extends the .button class

1  2  3  4  5  6  7  8  9  10  11  
.button {      background: green;    }    .sidebar .button {      font-size: 0.875em;    }    .button-2 {        @extend .button;    }

When the Sass is compiled, everywhere you see .button, you'll now see an additional selector that uses .button–2 in place of .button.

1  2  3  4  5  6  7  
.button, .button-2 {      background: green;    }    .sidebar .button, .sidebar .button-2 {      font-size: 0.875em;    }

What if you wanted .button–2 to extend .button only when it's not located inside the .sidebar? In other words you want the previous example to compile to:

1  2  3  4  5  6  7  
.button, .button-2 {      background: green;    }    .sidebar .button {      font-size: 0.875em;    }

You can create a silent class using a placeholder and extend the placeholder instead of the selector. For example:

1  2  3  4  5  6  7  8  9  10  11  12  
.button,    %button {      background: green;    }    .sidebar .button {      font-size: 0.875em;    }    .button-2 {        @extend %button;    }

In DRY fashion I added placeholder to the .button class and then extended the placeholder inside .button–2 instead of extending the class. Since the placeholder isn't present in the .sidebar .button selector, that selector won't be extended.

1  2  3  4  5  6  7  
.button, .button-2 {      background: green;    }    .sidebar .button {      font-size: 0.875em;    }

Now both .button and .button–2 will have a green background, but only .button will have a reduced font-size if it's inside the .sidebar.

The !optional Flag

We all make mistakes. Take the following where I'm extending a class called .nav-button without creating the .nav-button class.

1  2  3  
.nav-button-large {     @extend .nav-button;    }

In a case like this, Sass will throw an error and your code won't compile. At least that's the default. You can override that behavior with the !optional flag

1  2  3  
.nav-button-large {     @extend .nav-button !optional;    }

With the !optional flag set your Sass will compile to CSS despite the error. Of course, as there is no .nav-class to extend, their are no styles for .nav-button-large to inherit, which in this case means there's no .nav-button-large.

Using @extend inside Other Directives

You can use @extend directives inside other directives with some restrictions. For example Sass would need to create a lot of duplicate CSS so that rules created outside of a @media query can apply to selectors inside the @media query.

Because of that you can only use @extend within @media to extend selectors inside the same @media block. The restriction is the same for other directives.

This first block of code is fine, because .button-large extends a class inside the same @media directive.

1  2  3  4  5  6  7  8  9  10  
@media screen {     .button {       styles here;     }      .button-large {        @extend .button;        more styles here;      }  }

However, this block doesn't work since .button-large is now trying to extend a class outside of the @media directive.

1  2  3  4  5  6  7  8  9  10  
.button {     styles here;    }    @media screen {     .button-large {       @extend .button;       more styles here;     }  }

Closing Thoughts

Hopefully you can see the benefits of the @extend directive. They're a tool that makes use of inheritance and helps you produce DRYer CSS code with fewer classes and ids in your HTML.

When you use them in combination with placeholders you can abstract styles by create @extend-only selectors that are only used when needed making it easier to maintain themes and libraries.

There's one last directive I'd like to discuss before wrapping up this series about @-rules and directives. Next week I want to talk about the @mixin directive, which lets you define styles for reuse throughout your stylesheet.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now

Wednesday 10 February 2016

Sass: The @extend Directive - Vanseo Design

Sass: The @extend Directive - Vanseo Design


Sass: The @extend Directive

Posted: 09 Feb 2016 05:30 AM PST

Have you ever wanted one selector to inherit the styles of another? Maybe you have a .button class for standard buttons across the site and now you want to create a larger button that should be styled the same as the rest of your buttons, except it should be larger.

An Electric Power Strip

Wouldn't it be nice if you could tell the new class to style itself like standard buttons, but bigger. The @extend directive allows you do just that. It lets one selector inherit the styles of another selector, which you can then build on.

For the last few weeks I've been looking at @-rules and directives in Sass. I talked about @-rules in general and then more specifically about @media and @import. Last week I offered some ideas for structuring your Sass directories.

Today and next week I want to talk about the @extend directive, which as I mentioned allows one selector to inherit the styles of another and allowing you write DRYer code.

The @extend Directive

Let's think for a moment how you might style a standard .button class and then a .button-large class for a larger version of the standard button. A common approach is to create a .button class with the styles you want for your buttons and then add a new .button-large class that overrides the size.

1  2  3  4  5  6  7  
.button {     styles here;  }    .button-large {     styles to make .button larger;  }

You would add both classes to any elements that should become a large button.

1  
<a class="button button-large" href="">Click Me</a>

The downside is you now have an additional class to maintain in your stylesheet and to remember to include in your HTML.

The @extend directive provides another option that doesn't require a second class on your HTML. The directive allows you to extend one selector and tell Sass that the second selector should inherit the styles of the first.

Here's how you might style a standard button and then a large button using @extend. The size of the button is controlled by its font-size. If the font-size increases, the padding will also increase and both will lead to a larger button.

1  2  3  4  5  6  7  8  9  10  11  12  
.button {      background: #900;      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-large {      @extend .button;      font-size: 1.5em;    }

The .button class sets some general styles for how buttons across the site should look. The .button-large class extends the .button class through the @extend directive. It then overrides the font-size and makes it 50% larger.

The Sass compiles to the following CSS.

1  2  3  4  5  6  7  8  9  10  11  
.button, .button-large {      background: #900;      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .button-large {      font-size: 1.5em;    }

Notice that the styles aren't repeated in the .button-large class, but rather the selector has been added to the .button selector styles keeping the CSS DRY.

Creating a large button now requires a single class instead of two. Here the first line of HTML creates a standard button and the second line creates a large button. Each link requires a single class.

1  2  
<a class="button" href="">Click Me, I'm a button.</a>    <a class="button-large" href="">Click Me, I'm a large button.</a>

The selector that @extends and inherits styles will be inserted in DRY fashion wherever the extended selector exists in the stylesheet. For example imagine your buttons come in different colors and you defined those colors on a selector that requires the element to have both classes.

1  
<a class="button green" href="">Click Me</a>

Your CSS only adds the background color when both classes are present.

1  2  3  
.button.green {     background: #090;    }

You then extend the .button class inside a .button-large class as before.

1  2  3  
.button-large {     @extend .button;    }

A .button-large selector will be inserted wherever a .button selector exists so you'll find something like this in your compiled CSS file.

1  2  3  
.button.green, .button-large.green {     background: #090;    }

When selectors are merged this way, the @extend directive can avoid unnecessary duplication. For example if your compiled code would become:

1  2  3  
.button.button-large, .button-large.button-large {     styles here;    }

The duplicate would be removed.

1  2  3  
.button.button-large, .button-large {     styles here;    }

Extending Complex Selectors

You can extend more than classes. Any selector representing a single element can be extended. Imagine your links change background color and you want to do the same when hovering over a button.

1  2  3  4  5  6  7  
a:hover {      background: #c00;    }    .button-hover {      @extend a:hover;    }

Here I created a class called .button-hover that extends the :hover pseudo class and compiles to:

1  2  3  
a:hover, .button-hover {      background: #c00;    }

As a selector, a:hover is still relatively simple. You can extend more complex selectors such as .container main .button.

Multiple @extend Directives

A selector isn't limited to extending one other selector. Selectors can use multiple @extend directives and inherit the styles of several different selectors.

For example imagine you created classes for different background colors and all your large buttons will have a green background. Not exactly something you'd do in practice, but go with it for the example.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  
.button {      background: #900;      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .background-green {      background: #090;    }    .button-large {      @extend .button;      @extend .green;      font-size: 1.5em;    }

The .button-large class extends both .button and .green and then bumps up the font-size. The code compiles to:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  
.button, .button-large {      background: #900;      font-size: 1em;      padding: 0.5em 1.0em;      text-decoration: none;      color: #fff;    }    .background-green, .button-large {      background: #090;    }    .button-large {      font-size: 1.5em;    }

Another way to write multiple extends is by using a comma-separated list of selectors. In the previous example both extends could have been written as one.

1  
@extend .button, .green;

Either works. I prefer new lines because it's easier for me to read, though may find the single line easier to read.

Chaining @extend Directives

You can chain selectors so that one selector is extended by another selector, which is then extended by a third selector.

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  
a {     text-decoration: none;    }    .button {     @extend a;     background: #900;     font-size: 1em;     padding: 0.5em 1.0em;     color: #fff;    }    .button-large {     @extend .button;     font-size: 1.5em;    }

Here .button extends a link and is then extended by .button-large. The Sass compiles to:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  
a, .button, .button-large {     text-decoration: none;    }    .button, .button-large {     background: #900;     font-size: 1em;     padding: 0.5em 1.0em;     color: #fff;    }    .button-large {     font-size: 1.5em;    }

Selector Sequences

At the moment you can't extend a sequence of selectors. For example the following doesn't work.

1  2  3  4  5  6  7  
.sidebar .button {     styles here;    }    .button-large {     @extend .sidebar .button    }

However a selector sequence can extend a single selector.

1  2  3  4  5  6  7  8  
.button {     styles here;    }    .sidebar .button-large {     @extend .button     styles here;    }

This is perfectly valid and compiles to:

1  2  3  4  5  6  7  
.button, .sidebar .button-large {     styles here;    }    .sidebar .button-large {     styles here;    }

Similarly you can extend one part of a selector sequence. Here .button-large extends only the .button class part of the sequence.

1  2  3  4  5  6  7  8  
.sidebar .button {     styles here;    }    .button-large {     @extend .button;     additional styles here;    }

When the Sass is compiled every instance where you would find .button, you'll now also find a duplicate using .button-large instead.

1  2  3  4  5  6  7  
.sidebar .button, .sidebar .button-large {     styles here;    }    .button-large {     styles here;    }

Merging Selector Sequences

Extending sequences of selectors can get complex in a hurry. Consider the following example.

1  2  3  4  5  6  7  
.page .sidebar .button {     styles here;    }    .container .footer .button-large {     @extend .button    }

The second selector is extending .button. There are more ways the selectors above can be merged than you might think at first. The obvious way is to substitute the entire second selector for .button in the first selector.

1  
.page .sidebar .container .footer .button-larg

However, the following selector is also valid.

1  
.container .footer .page .sidebar .button-larg

As long as .button-large comes before .page .sidebar the selector will be valid. It turns out there are 10 possible ways to merge these selectors, though not all of them are likely to be used. Instead of adding all 10 on compile, Sass generates only those likely to be useful according to the following two rules.

  • When the two sequences being merged have no selectors in common, two new selectors are generated: one with the first sequence before the second, and one with the second sequence before the first.

Following this rule the previous example compiles to:

1  2  3  4  5  
.page .sidebar .button,    .page .sidebar .container .footer .button-large,    .container .footer .page .sidebar .button-large {     styles here;    }
  • When the two sequences share some selectors, those selectors will be merged together and only the differences (if any still exist) will alternate.

Here I changed the previous example and replaced .container in the second selector to .page so the two will share a selector.

1  2  3  4  5  6  7  
.page .sidebar .button {     styles here;    }    .page .footer .button-large {     @extend .button    }

The Sass compiles to the following.

1  2  3  4  5  
.page .sidebar .button,    .page .sidebar .footer .button-large,    .page .footer .sidebar .button-large {     styles here;    }

The only difference in the sequence (besides the change from .button to .button-large) is that one selector contains .sidebar and the other contains .footer. These will alternate positions as you can see in the compiled code.

Closing Thoughts

Inheriting the styles of one selector into another helps make maintenance easier by having a single source for those styles. Inheritance in general reduces duplicate code and helps make your code reusable. Sass maintains the lack of duplication by compiling to DRY CSS.

If you've ever styled one selector to look similar, though not quite the same as another, you'll appreciate being able to inherit the styles using @extend.

There's more I want to cover about the @extend directive and I'll finish next week, including what to do when you want to inherit the styles of one class, but you don't actually want to compile the code of that class.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now

Wednesday 3 February 2016

Sass: Directory Structures That Help You Maintain Your Code - Vanseo Design

Sass: Directory Structures That Help You Maintain Your Code - Vanseo Design


Sass: Directory Structures That Help You Maintain Your Code

Posted: 02 Feb 2016 05:30 AM PST

As projects grows larger, the need to modularize directory and file structure increases. This is particularly true if you want to create code that can be used in other projects, in components and patterns across one or more projects, or in templates you share with others.

Hexanol Structure
Structure of a 2-Hexanol molecule

Last week I talked about the @import directive and how you could use it to create a more maintainable file and folder structure. I showed you one simple structure I learned and I promised to show you some other structures I've come across.

I have a few to share and I'll talk about some of the commonalities between them to help you choose one or develop a structure that works for you.

Simple Structure

I hope you don't mind if I start with the simple directory structure I mentioned last week. Here it is as a reminder.

  • modules/
    • _color.scss
    • _typography.scss
  • partials/
    • _base.scss
    • _navigation.scss
  • vendor/
    • _ico-moon.scss
  • main.scss

The vendor folder holds 3rd party code. The partials folder is for code you want to include and compile. The modules folder is for code you want to include, but don't want to compile.

Note that this structure allows you to distinguish between your code and 3rd party code and it allows you to distinguish between code you want to compile and code you don't want to compile. That's what this organization does.

The names of the files and folders can be changed to whatever you want. The key is this structure allows you to keep your code separate from 3rd party code and it allows you to separate your code between that which should be compiled into CSS and that which shouldn't be compiled.

The 7–1 Pattern

Another structure I came across is the 7–1 pattern from Hugo Giraudel. Here's how you might set up a directory along with some examples of files that would be in each folder.

  • base/
    • _reset.scss
    • _typography.scss
    • _color.scss
  • components/
    • _buttons.scss
    • _navigation.scss
    • _gallery.scss
  • layout/
    • _header.scss
    • _grid.scss
    • _sidebar.scss
  • pages/
    • _home.scss
    • _about.scss
    • _contact.scss
  • themes/
    • _theme.scss
    • _admin.scss
  • helpers/ (or utils/)
    • _variables.scss
    • _functions.scss
    • _mixins.scss
  • vendors/
    • _bootstrap.scss
    • _jquery-ui.scss
  • main.scss

The base folder holds boilerplate content. It holds the styles every page of your site should receive.

The components folder holds all your micro layout files. Your styles for buttons and navigation and similar page components.

Your macro layout files go in the layouts folder. Styles for major sections of the layout like a header or footer and styles for a grid system would belong here.

If you have styles specific to individual pages on your site, you can place them in the pages folder. For example it's not uncommon for the home page of your site to require page specific styles that no other page receives.

The themes folder holds files that create project specific themes. For example one section of your site might use a color scheme with primary colors, while another section builds a color scheme based on neutrals and earth tones.

The helpers or utils folder holds Sass tools, helper files, variables, and config files. These files won't be compiled.

Finally the vendors folder holds 3rd party code and the main.scss file uses @import statements to include the other files.

Here are some additional resources for this pattern

SMACSS/BEM Architecture

A number of people have written about setting up directory structures based on either a SMACSS or BEM architecture. I want to present four different, albeit similar, structures that build on each other and also incorporate some ideas from the 7–1 pattern.

Ryan Burgess leveraged some ideas from SMACSS when setting up this simple structure for Evernote. They used four folders, Base, Layout, Modules, and Themes

  • Base holds vendor code, helpers, mixins, variables, and general selector styles (body, h1, p, a, etc.).
  • Layout holds macro layout styles.
  • Modules holds micro layouts styles.
  • Themes holds any type of page specific styles.

Notice the distinction between micro and macro layout as well as the distinction for page specific styles and a general catchall for files with non-compiled code and general styles.

Tim Hartmann and Bram Smulders shared similar four folder structures.

  • Base holds vendor code, helpers, mixins, variables, and general selector styles (body, h1, p, a, etc.).
  • Layout holds macro layout styles.
  • Modules holds micro layout styles.
  • State holds state specific code, how buttons look on hover and similar.

Again there's the distinction between macro and micro layout styles as well as a folder that holds non-compiled code and general styles. The difference here is a State folder for state styles instead of a Themes folder for page specific styles.

Matt Staffer shared the organization his company used to help them keep a SMACSS-inspired structure.

  • _base.scss is for vendor code and styles on base elements (html, body, ul, p, etc.).
  • _layout.scss is for macro layout styles.
  • _modules.scss is for micro layout styles.
  • _other.scss is for whatever doesn't fit in first three.
  • _shame.scss is for code you feel ashamed of and plan to improve.

It's similar to the previous two structures, though note that these are files and not folders. The _other.scss file is the catchall and there's a _shame.scss file to nag you into improving the code it contains.

One last SMACSS based organization comes from Johannes Dreller. It adds another directory to the ones we've seen so far. Here's Johannes' structure along with some files each might hold.

  • vendor/
    • _bootstrap.scss
    • _jquery-ui.scss
  • base/
    • _functions.scss
    • _mixins.scss
    • _variables.scss
    • _base.scss
  • layout/
    • _grid.scss
    • _header.scss
    • _sidebar.scss
  • module/
    • _navigations.scss
    • _buttons.scss
    • _forms.scss
  • state/
    • _state.scss
  • hacks/
    • _shame.scss
  • main.scss

The addition is the vendor folder, which is used to hold 3rd party content. The layout and module folders still distinguish macro and micro layout styles. State changes go in the state folder. Non-compiled code and general styles go in the base folder. The hacks folder takes the shame file and there's a main.scss to @import all the necessary files.

Others

Matthieu Larcher & Fabien Zibi present DoCSSa's four folder organization.

  • base/
    • __base.scss
    • _config.scss
    • project/
    • utils/
  • components/
    • _components.scss
    • button/
    • tabs/
  • specifics/
    • _specifics.scss
    • modal.scss
    • navigation.scss
  • vendor/
    • _vendor.scss

This structure is similar to the others I've mentioned. The base folder holds boilerplate styles in the project folder and Sass tools in the utils folder. The files directly in the base folder are for code needed in both subfolders.

Components is for micro layout and specifics is a catchall for most everything else. In both cases the files directly in the folder are for those used in the subfolders. Finally there's a vendor folder for 3rd party code.

The last structure I'll mention comes from Matthew Anderson and it adds some new ideas for organizing the directory.

  • framework/
  • modules/
  • vendor/
  • sections/
  • _bootstrap.scss
  • _section.scss

First the familiar. The vendor folder holds 3rd part code, modules holds partials that contain reusable UI patterns and components. The framework folder holds all project wide and ideally project agnostic code.

The sections folder is similar to the shame or hacks folder. It's not necessarily code you're ashamed of, but it's code you've yet to optimize or refactor or generally improve.

The two files are new. _bootstrap.scss isn't from Twitter's Bootstrap. It's a file that bootstraps the necessary files through @import statements.

_section.scss represents any file will become a compiled CSS stylesheet. These files will import _bootstrap.scss as well as any other project files necessary.

Closing Thoughts

I hope seeing examples of how others organize their Sass files and folders helps you understand some of the ways people modularize their code to keep it organized and maintainable.

What I hope you to take away from the structures I've mentioned is the idea that there's no single way to organize your stylesheets and directories, though there are common themes in the organization others have come up with.

  • Micro and macro layout styles
  • Boilerplate (global site) styles
  • Tools and utility files
  • Page and site theme specific styles
  • 3rd party code
  • Shame files

These are some of the common things that others think worth keeping separate. You might decide you don't need to keep macro and micro layout styles separate and prefer to keep them together. You might not use 3rd part code on a project. See these commonalities as suggestions and not anything you absolutely have to use.

You can organize your files and folders however you think best. Just keep in mind that you should organize them in some meaningful way that helps you find and isolate your styles and code in a way that makes sense to you.

Next week I want to talk about another Sass directive, @extend. It allows a selector to inherit styles from other selectors so you can write DRYer code.

Download a free sample from my book Design Fundamentals.

Join me as I share my creative process and journey as a writer.

This posting includes an audio/video/photo media file: Download Now