Documentation

vary is currently supported on Sass 3.4+ and libsass 3.2+. Here is what you can find under the hood:

All of these functions and mixins have been built to be used independently in case it was ever needed.

If you reckon there's a better way of achieving that, please open an issue or send a pull request.

Variables

To get started with vary you will only need to set up your $vary-map map properly, but there are some other options that you may find interesting.

$vary-libsass

Set to false if not using libsass. Uses some extra CSS on HTML class creation mode.

$vary-libsass: true !default;

$vary-default-prefix-class

When creating a class, vary will use this prefix. Will work as .#{$vary-default-prefix-class}foo

$vary-default-prefix-class: 'is-' !default;

$vary-default-create

Default creation mode as default value for $create. Can be changed for your convenience.

$vary-default-create: 'modifier' !default;

$vary-map

Map of entities. Should be defined with your other variables, before the mixins.

Set as many entities as you wish, will be used to keep consistency. The variables under each key should be consistent in order to loop over all the keys.

$vary-map: (
  user: (
    'color-primary': blue,
    'border': '1px solid #ff0'
  ),
  admin: (
    'color-primary': pink,
    'border': '10px solid black'
  )
) !default;

$vary-loop-entity

Current *entity* during the loop. To be used in custom creation mode.

$vary-loop-entity: '';

$vary-loop-map

Current _map_ during the loop. To be used in custom creation.

$vary-loop-map: $vary-map;

Functions

Some of these functions could come in handy, but the one you may be especially interested in is vary-get, since it'll let you access values outside the vary scope.

vary-get()

Gets a value from a vary-like map using the current entity and map withing the vary scope (or not).

Takes the current entity and map within the vary scope.

@function vary-get($key, $entity: $vary-loop-entity, $map: $vary-loop-map) {
  $entity-map: map-get($map, $entity);

  @if not $entity-map {
    @error 'There is no `#{$entity}` entity in your variations list.';
  }

  $ret: map-get($entity-map, $key);

  @if not $ret {
    @error 'The entity `#{$entity}` doesn\'t have a value for `#{$key}`.';    
  }

  @return unquote($ret);
}
vary-get() example
$vary-map: (
  user: (
    'color-primary': blue,
    'border': '1px solid #ff0'
  ),
  admin: (
    'color-primary': pink,
    'border': '10px solid black'
  )
);

.foo{
	background: vary-get('color-primary', admin);
}
.foo{
	background: pink;	
}

vary-filter()

Works out the list of entities to go through based on the map, the targeted and the excluded.

@function vary-filter($included: (), $excluded: (), $map: $vary-loop-map) {
  $ret: ();
  
  // If haven't specified anything, default to all
  @if $included == 'all' {
    $included: map-keys($map);
  }

  // Build the specified key list
  @each $key in $included {

    // If it's in our map
    @if not map-has-key($map, $key) {
      @error 'There is no `#{$key}` class in your variations list.';
    } 

    // But not excluded
    @if not index($excluded, $key) {
      // Add it to the final list
      $ret: append($ret, $key, 'comma');
    }
  }

  @return $ret;
}
vary-filter() example
vary-filter(color border thickness, border);
// -> (color thickness)

vary-htmlclass()

Adds a body class between the html class and the rest of the selector.

@function vary-htmlclass($selector-list, $bodyclass) {
  $ret: ();

  // Go through each selector
  @each $selector in $selector-list {
    $with-bodyclass: ();

    // Just html class - .htmlclass
    $with-bodyclass: append($with-bodyclass, nth($selector, 1));

    // Add body class - .htmlclass .bodyclass
    $with-bodyclass: append($with-bodyclass, unquote($bodyclass), 'space');

    // If more than one element
    @if length($selector) > 1 {
      // Each restant element - .htmlclass .bodyclass .otherselectors
      @for $i from 2 through length($selector) {
        // Add the other selectors
        $with-bodyclass: append($with-bodyclass, nth($selector, $i), 'space');
      }
    }

    // Merge it all in the selector list
    $ret: append($ret, $with-bodyclass, 'comma');
  }

  @return $ret;
}
			
vary-filter() example
vary-htmlclass(
	(.js .foo, .csstransitions .element), 
	".is-admin"
);
// -> (
	.js .is-admin .foo, 
	.csstransitions .is-admin .element
)

Mixins

Each creation mode has a mixin that performs its dedicated action on a single entity. It is vary itself which will loop through the entities and call the required mixins.

vary-single-htmlbody()

Creates variations with a .is-class body class. It also places it after the HTML class given in the parent.

@mixin vary-single-htmlbody($entity) {
  // Update the global helper class
  $vary-loop-entity: $entity !global;
  $selector-list: ();

  @if not & {
    @error 'No parent found. I need the parent HTML class to place everything on its right place.';
  }

  $selector: &;
  $bodyclass: '.#{$vary-default-prefix-class}#{$vary-loop-entity}';
  
  @at-root{
    @if $vary-libsass {
      html {
        &#{vary-htmlclass($selector, $bodyclass)} {
          @content;
        }
      }
    } @else {
      #{vary-htmlclass($selector, $bodyclass)} {
        @content;
      }
    }
  }

}

vary-single-parent()

Creates variations with a .is-class parent/body class.

@mixin vary-single-parent($entity) {
  // Update the global helper class
  $vary-loop-entity: $entity !global;

  @at-root{
    @if & {
      .#{$vary-default-prefix-class}#{$entity} & {
        @content;
      }
    } @else {
      .#{$vary-default-prefix-class}#{$entity} {
        @content;
      }
    }
  }

}

vary-single-append()

Appends an .is-class class to the element.

@mixin vary-single-append($entity) {
  // Update the global helper class
  $vary-loop-entity: $entity !global;

  @if not & {
    @error 'No selector found. I need a selector to append the class to.';
  }

  &.#{$vary-default-prefix-class}#{$entity} {
    @content;
  }
}

vary-single-modifier()

Creates BEM modifiers for a wrapper element

@mixin vary-single-modifier($entity) {
  // Update the global helper class
  $vary-loop-entity: $entity !global;

  // Do we have a parent selector?
  @if not & {
    @error 'You can\'t create a modifier without a base.';
  } 

  @at-root {
    &--#{$entity} {
      @content;
    }
  }
}

vary-single-custom()

Doesn't create anything, it just gives you the right scope.

@mixin vary-single-custom($entity) {
  // Update the global helper class
  $vary-loop-entity: $entity !global;
  
  @content;
}

vary()

Creates variations for $vary-map.

@mixin vary($create: $vary-default-create, $for: 'all', $not: (), $loop: $vary-map){
  $vary-loop-map: $loop !global;
  $entity-list: vary-filter($for, $not, $loop);

  $creation-modes: parent, insert, modifier, append, custom;

  // Check if valid creation mode
  @if not index($creation-modes, $create) {
    @error 'There is no `#{$create}` creation mode in this version of vary.';
  }

  // Go through all the creation modes
  @if $create == 'parent' {
    // Body class
    @each $entity in $entity-list {
      @include vary-single-parent($entity) {
        @content;
      }
    }
  } @elseif $create == 'insert' {
    // Body class with html parent class
    @each $entity in $entity-list {
      @include vary-single-htmlbody($entity) {
        @content;
      }
    }
  } @elseif $create == 'modifier' {
    // BEM modifier
    @each $entity in $entity-list {
      @include vary-single-modifier($entity) {
        @content;
      }
    }
  } @elseif $create == 'append' {
    // Appended class
    @each $entity in $entity-list {
      @include vary-single-append($entity) {
        @content;
      }
    }
  } @else {
    // Custom
    @each $entity in $entity-list {
      @include vary-single-custom($entity) {
        @content;
      }
    }
  }
}