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).
.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.
.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.
.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.
.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.
@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).
.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.
.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.
$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!