Functional Wrappers in Sass

Originally published at:

I am not a big fan of having global variables all over the place, no matter the language. In Sass, as in CSS, we have nothing but a global namespace (although latest Sass versions progressively provide more scoping features). It’s like a house with a single room: no matter how hard you try to organize your furniturs, at the end of the day there is still only a single room in the house.

What’s the problem with this? When it comes to Sass, the problem is that variables can overlap. What if you have a variable called $baseline, and a Sass grid framework you use have a $baseline variable as well? Or worst, what if a Sass library you included uses a $_ variable to hold private content required for it to work and you happen to override it yourself with your own stuff? Simple: it breaks.

Coming back to my initial point: I am not a big fan of having variables to configure a Sass system. I shall be more precise and say “having only variables” because, of course, variables are the key when it comes to store data. Meanwhile, it has occurred to me that functional wrappers help a lot.

Functional what…?

You could think of function wrappers as getters and setters in a way that they are functions wrapping a variable declaration or retrieving to add some extra features on top of it: validation, error handling, default values, warnings, and so on.

Isn’t it a pain to do this?, you say. It depends. There is something beautiful in a variable: its simplicity. A variable is obvious: it is a value mapped to a storage key. How could it get any simpler than this? Yet, I think raw variables are not best suited to handle things like library configurations and such.

Actually the choice is yours: either you have a function wrapper that handles everything from the very start (errors, parameter validation, default values…), or you make all of this in every functions and mixins you’ve got. I’ll play clear: I’d rather go the former.

How does it work?

Continue reading this article on SitePoint

Thank you! Great article. But is it mistake?
$main-direction: if($writing-mode != ‘LTR’, right, left); // maybe if($writing-mode != ‘RTL’, left, right);
$opposite-direction: if($writing-mode != ‘LTR’, left, right);

and this:
@function get-layout-opposite-direction() {
@return if(get-content-direction() == “RTL”, left, right); // maybe @return if(get-content-direction() == “RTL”, right, left);

Sorry, if I’m wrong.


If writing mode is not LTR (thus RTL), main direction is right. Else it’s left.
If get-content-direction returns RTL, then opposite direction is left. Else it’s right.

Hi Hugo,

I think the example is a bit overcomplicated. IMO a get/set function will suffice.

  // 1 Value is passed – attempt to set
  // 2 Format input
  // 3 Validate input
  // 4 Valid   => set global  
  // 5 Invalid => throw error
  // 6 Default value (or set outside function)
  // 7 Return value
  @function content-direction($direction: null){
    @if $direction != null {                    // 1  
      $direction: to-lower-case(#{$direction}); // 2
      @if index(rtl ltr, $direction) {          // 3 
        $__direction: $direction !global;       // 4 
      } @else {                                 // 5
        @error "Content direction can be either "
             + "`LTR` or `RTL` (case-insensitive). "
             + "Given: `#{$direction}`. Aborting.";
    $__direction: ltr !default;                 // 6
    @return $__direction;                       // 7

A second note (adding to your crusade against @extends ;))
be careful when using variables in placeholders. The value
is set at placeholder position – not on (first) extend.

// Define content-direction
@include content-direction("RTL");

// Framework stuff
%grid-whatever-tool {
    margin-#{get-layout-direction()}: 1em;

// Redefine content-direction 
@include content-direction("LTR");

// Extend 
.class {
  @extend %grid-whatever-tool;
  expected: get-layout-direction();

// CSS (not so margin-left as one might expect)
.class {
  margin-right: 1em;
.class {
  expected: left;


This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.