Demo setup

In order to keep the demos neat and tidy, I'll use the same map across all of them. As follows:

$vary-map: (
  user: (
    'color-primary': blue,
    'border': '1px solid #ff0'
  ),
  admin: (
    'color-primary': pink,
    'border': '10px solid black'
  ),
  superadmin: (
	'color-primary': black,
	'border': '.1em dashed red'
  )
);

Each demo will have an explained piece of Sass code and CSS result, with a SassMeister link in the caption so you can play around with the code.

Creation modes

To set a creation mode, use the $create parameter on vary. There are 5 creation modes available at the moment, including a custom one. If you see yourself using one a lot, you can set the default creation mode with $vary-default-create.

Parent - parent/body class

The most common way to approach CSS theming is using a parent/body class.

By setting the $create parameter to parent, we are telling vary to create body classes for us.

The body class will be built based on a prefix and the current entity in the loop. By default, is- will be used as the prefix, so the class would be .is-entity.

If you want to change the prefix you can do so by setting $vary-default-bodyclass.

The body class will always be placed outside the entire selector. If an HTML class is wrapping the selector, you will need to use the body with HTML class wrapper (see next creation mode).

Body class example - See on SassMeister
.foo .example{
	background: red;

	@include vary($create: parent) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}
}
.foo .example{
	background: red;	
}

.is-user .foo .example{
	background: blue;
	border: 1px solid #ff0;
}

.is-admin .foo .example{
	background: pink;
	border: 10px solid red;
}

.is-superadmin .foo .example{
	background: black;
	border: .1em dashed red;
}

Insert - Body with HTML class wrapper

You'll need to use this approach if you use Modernizr.

If you need to apply the body class on a selector that contains an HTML class while following the same functionality as the body class mode, set the $create parameter to insert.

Using the vary-htmlclass function, vary builds a selector formed by the HTML class, then the body class and then the rest of the selector.

The HTML class will always be considered the first class in the selector. In this example, .js.

Note: The html tag is only added as a fix for libsass. If you don't use libsass, you can prevent this behaviour by setting $vary-libsass: false; on your variables list.

Body class with HTML class wrapper example - See on SassMeister
.js .foo{
	background: red;

	@include vary($create: insert) {
		background: vary-get('color-primary');
	}
}
.foo{
	background: red;	
}

html.js .is-user .foo{
	background: blue;
}

html.js .is-admin .foo{
	background: pink;
}

html.js .is-superadmin .foo{
	background: black;
}

Modifier (BEM)

For when differently styled modules need to be present on the same page.

In this case, the most common approach is to create a modifier. To define it, you will need the block of which vary will create the modifiers. For code quality reasons, the BEM modifier is placed at the same scope as the element.

BEM modifier example - See on SassMeister
.parent .foo{
	background: red;

	@include vary($create: modifier) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}
}
.parent .foo{
	background: red;	
}

.parent .foo--user{
	background: blue;
	border: 1px solid #ff0;
}

.parent .foo--admin{
	background: pink;
	border: 10px solid red;
}

.parent .foo--superadmin{
	background: black;
	border: .1em dashed red;
}

Append

If you don't want to use BEM, this is your approach for the same scenario.

Appended class example - See on SassMeister
.parent .foo{
	background: red;

	@include vary($create: append) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}
}
.parent .foo{
	background: red;	
}

.parent .foo.is-user{
	background: blue;
	border: 1px solid #ff0;
}

.parent .foo.is-admin{
	background: pink;
	border: 10px solid red;
}

.parent .foo.is-superadmin{
	background: black;
	border: .1em dashed red;
}

Custom

A vary playground. Feel free to create any kind of construction.

Keep in mind these two variables:

  • $vary-loop-entity: Contains the value of the current entity on the loop (e.g. user, admin).
  • $vary-loop-map: Contains the map that vary is looping through (e.g. $vary-map).

If you see yourself using the same custom pattern a lot, please open an issue or send a pull request and I'll include it as a mode.

Body class with HTML class wrapper example - See on SassMeister
@include vary($create: custom) {
	@at-root{
		.#{$vary-loop-entity}-foo {
			background: vary-get('color-primary');
			border: vary-get('border');
		}
	}
}
.user-foo{
	background: blue;
	border: 1px solid #ff0;
}

.admin-foo{
	background: pink;
	border: 10px solid red;
}

.superadmin-foo{
	background: black;
	border: .1em dashed red;
}

Flexibility

vary is highly customizable, that's why it has some other options besides the creation mode. Let's imagine different scenarios where you may need some flexibility.

Targeting

Using the $for parameter to target specific entities.

This way you can make vary go through some of the entities from them map, not all of them.

If any of the entities given are not on the map, vary will throw an error. The rest of the entities will be ignored.

If you need to set this behaviour for these entities but a different one for the rest of them, you need to exclude them (see next).

Targeted entities example - - See on SassMeister
.foo .example{
	background: red;

	@include vary($create: parent, $for: user admin) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}
}
.foo .example{
	background: red;	
}

.is-user .foo .example{
	background: blue;
	border: 1px solid #ff0;
}

.is-admin .foo .example{
	background: pink;
	border: 10px solid red;
}

.is-superadmin .foo .example{
	background: black;
	border: .1em dashed red;
}

Excluding

The reversed version of the targeted ones.

Using the $not parameter, you can exclude entities so vary doesn't loop through them.

vary will loop through every entity but the ones listed in the $not list, so a combination of one vary looping throught the targeted ones and then another without them will cover every single entity in the map.

In this example, the same result could have been achieved by using the $for parameter with a superadmin value, but if new entities were added to the map, they would not be covered.

Excluded entities example - See on SassMeister
.foo .example{
	background: red;

	@include vary($create: parent, $for: user admin) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}

	@include vary($create: modifier, $not: user admin) {
		background: vary-get('color-primary');
		border: vary-get('border');
	}
}
.foo .example{
	background: red;	
}

.is-user .foo .example{
	background: blue;
	border: 1px solid #ff0;
}

.is-admin .foo .example{
	background: pink;
	border: 10px solid red;
}

.foo .example--superadmin{
	background: black;
	border: .1em dashed red;
}

Multiple maps

By default, vary will loop through the entities in the $vary-map map.

But if you have multiple maps with the required structure (entity, list of values), you can set any map using the $loop parameter and vary will loop through it instead of the default one.

Remember that you can use the current entity ($vary-loop-entity) on any vary loop. In this case, it's been used as the content property to actually display the text.

Multiple map support example - See on SassMeister
$stages: (
	kid: (
		'color': blue
	),
	teen: (
		'color': pink
	),
	adult: (
		'color': green
	),
	elder: (
		'color': grey
	)
);

.foo{
	background: red;

	@include vary($create: modifier, $loop: $stages) {
		background: vary-get('color');

		&:before{
			content: '#{$vary-loop-entity}';
		}
	}
}
.foo{ background: red; }

.foo--kid{ color: blue; }
.foo--kid:before{
	content: 'kid';
}

.foo--teen{ color: pink; }
.foo--teen:before{
	content: 'teen';
}

.foo--adult{ color: green; }
.foo--adult:before{
	content: 'adult';
}

.foo--elder{ color: grey; }
.foo--elder:before{
	content: 'elder';
}

In case you didn't notice, you can combine all this flexibility with any creation mode. So go nuts! Don't forget to open an issue or send a pull request if you find something odd.

vary uses some internal functions that you may find useful too. Take a look at the docs!