Skip to main content

Mixins

Overview

Both Sass and Less have mixins, and Jess supports the full features of both. But let's start with the basics, if mixins are new to you. Think of mixins as a reusable block of styles that you can drop anywhere.

Jess supports Less-style mixin names (.mixin or #mixin) and Sass-style mixin names (mixin). Mixins are followed by parentheses.

mixin-1() {
color: red;
}

Mixins are included ("mixed in") to other rules using the > operator.

For example, if we have the following mixin:

// mixin.jess
mixin-1() {
color: red;
}
.box {
$ > mixin-1();
}

...the CSS output will be:

.box {
color: red;
}

Selectors in Mixins

Mixins can contain more than just properties; they can contain other rulesets too.

For example:

// main.jess
.my-hover-mixin() {
&:hover {
border: 1px solid red;
}
}
button {
$ > .my-hover-mixin();
}

// main.css
button {
&:hover {
border: 1px solid red;
}
}

...or when collapsing is set to true

// main.css
button:hover {
border: 1px solid red;
}

Chaining mixin calls

You can chain mixin calls together. If the mixin within the chain doesn't require any arguments, you can omit the parentheses. (Parentheses are still required at the end of the chain to output the final value.)

#ns() {
.mixin() {
.inner-mixin() {
color: red;
}
}
}
.box {
$ > #ns > .mixin > .inner-mixin();
}

Multiple matching mixins

Note that in Jess, like Less, because we declare selectors and mixins in similar ways, we also can have multiple matches for mixins.

Meaning, just like CSS selectors, we can include multiple mixins with the same name in the same ruleset.

.my-mixin() {
color: red;
}
.my-mixin() {
color: blue;
}
.my-mixin() {
color: green;
}
.box {
$ > .my-mixin();
}

.box {
color: red;
color: blue;
color: green;
}
How Jess & Less overload mixins

Mixins do not need to be defined within the same scope, nor do mixin overloads need to be sequential rules. When a mixin is called, Jess looks up the scope tree by name to find potential matches. When it does, it evaluates arity (a match in the number of arguments) and any guards to see if one or more of the mixins is callable.

Parameters and arguments

Mixins can make repeated rules and configurability trivial, especially when you begin using parameters.

button-base($bg: #1a73e8, $color: #fff, $pad: 0.5em 1.25em) {
display: inline-block;
background: $bg;
color: $color;
padding: $pad;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
transition: background 0.15s;
&:hover { background: $less.color.darken($bg, 7%); }
&:active { background: $less.color.darken($bg, 14%); }
}

.button-primary { $ > button-base(); }
.button-secondary { $ > button-base(#fff, #1a73e8, 0.5em 1.25em); }
.button-danger { $ > button-base(#c62828, #fff, 0.5em 1.25em); }

Let's break this down.

The mixin definition

button-base($bg: #1a73e8, $color: #fff, $pad: 0.5em 1.25em) {
...
}
  • Parameters:
    • bg: background color (default: #1a73e8)
    • color: text color (default: #fff)
    • pad: padding (default: 0.5em 1.25em)
  • Default values:
    If you don’t pass a value for a parameter, Jess uses the default you set in the definition.
Comma separators

Like CSS, Jess uses commas (,) between mixin and function arguments (and parameters). If you need to use a CSS value list that has a comma, you can assign it to a variable to pass it in, or you can wrap it in escaped parentheses like Less does e.g. ~(one, two, three)

$values: one, two, three;
$ > button-base($values);

// or
$ > button-base(~(one, two, three));

Using the mixin

.button-primary   { $ > button-base(); }
.button-secondary { $ > button-base(#fff, #1a73e8, 0.5em 1.25em); }
.button-danger { $ > button-base(#c62828, #fff, 0.5em 1.25em); }
  • How to use:
    Use $ > [mixin name](arguments...) to inject the mixin’s styles.
  • Using defaults:
    Calling button-base() with no arguments means all defaults are used.
  • Overriding values:
    Pass any or all values, in order, separated by commas, to override the defaults.
    • For example:
      $ > button-base(#fff, #1a73e8, 0.5em 1.25em);
      sets a white background, blue text, and custom padding for .button-secondary.
  • Named arguments:
    You can specify which parameter you’re overriding by name, and skip the rest:
    .button-skinny { $ > button-base($pad: 0.25em 1em); }
    This sets only the padding, and leaves the background and color at their default values.

Mixins without default values

You don’t have to give parameters default values. If you leave them out, Jess requires you to provide all arguments when calling this mixin.

Example

highlight($bg, $color) {
background: $bg;
color: $color;
}
.alert-success { $ > highlight( #e5fbee, #26734d ); }
.alert-error { $ > highlight( #ffeaea, #b71c1c); }

Mixin guards

Jess supports mixin guards, which are a feature in Less as well. Guards are useful when you want to match on expressions, as opposed to simple values or arity. If you are familiar with functional programming, you have probably encountered them already.

Guards are designed to be similar to CSS Media Query syntax.

Let's start with an example:

mixin($a) when ($color.lightness(a) >= 50%) {
background-color: black;
}
mixin($a) when ($color.lightness(a) < 50%) {
background-color: white;
}
mixin($a) {
color: $a;
}

The key is the when keyword, which introduces a guard sequence (here with only one guard). Now if we run the following code:

.class1 { $ > mixin(#ddd); }
.class2 { $ > mixin(#555); }

Here's what we'll get:

.class1 {
background-color: black;
color: #ddd;
}
.class2 {
background-color: white;
color: #555;
}

Guard comparison operators

The full list of comparison operators usable in guards are: >, >=, =, =<, <. Additionally, the keyword true is the only truthy value, making these two mixins equivalent:

truth($a) when ($a) { ... }
truth($a) when ($a = true) { ... }

Unlike JavaScript, any value other than an expression that evaluates to true or the keyword true is considered false:

.class {
$ > truth(40); // Will not match any of the above definitions.
}

Note that you can also compare arguments with each other, or with non-arguments:

$theme: "brand";

button-theme($bg, $color) when ($theme = "brand") {
// ...
}

button-theme($bg, $color) when ($theme = "minimal") {
// ...
}

max($a, $b) when ($a > $b) { width: $a }
max($a, $b) when ($a < $b) { width: $b }
info

A when guard (or $if) condition is already an expression, so you write the comparison directly inside (...): variables keep their $ sigil, and there is no $(...) wrapper (that would be an expression inside an expression).

max($a, $b) when ($a > $b) { width: $a }
truth($a) when ($a = true) { ... }

Bare identifiers inside an expression are keywords, not variables — so true is already a keyword and needs no escaping.

Guard logical operators

You can use logical operators with guards. The syntax is based on CSS media queries.

Use the and keyword to combine guards:

mixin($a) when ($type.isnumber($a)) and ($a > 0) { ... }

Use the word or for alternate conditions. If any of the guards evaluate to true, it's considered a match:

mixin($a) when ($a > 10) or ($a < -10) { ... }

Use the not keyword to negate conditions:

mixin($b) when not ($b > 0) { ... }

Default mixins

If after finding potential mixin matches, none of the when guards evaluate to true, you may want to have a default mixin to call, much like the default statement in a switch block in JavaScript and other languages.

mixin(1)               { x: 11 }
mixin(2) { y: 22 }
mixin($x) when default { z: $x }

div {
$ > mixin(3);
}

div.special {
$ > mixin(1);
}

Output:

div {
z: 3;
}
div.special {
x: 11;
}

It is possible to use the value returned by default with guard operators. For example mixin() when not default {} will match only if there's at least one more mixin definition that matches the mixin() call:

mixin($value) when ($type.ispixel($value))  { width: $value }
mixin($value) when not default { padding: $(value / 5) }

div-1 {
$ > mixin(100px);
}

div-2 {
/* ... */
$ > mixin(100%);
}

div-1 {
width: 100px;
padding: 20px;
}
div-2 {
/* ... */
}

Selectors as Mixins

Jess lets you "apply" or call rulesets as mixins with the $apply keyword.

This is great when you want to define a class you can use on its own, but also “pull in” those same styles somewhere else.

Example

.rounded {
border-radius: 8px;
overflow: hidden;
}

.card {
box-shadow: 0 2px 8px #0002;
padding: 1.5em;
background: #fff;
$apply .rounded;
}
Applying multiple selectors

You can apply multiple selectors at once by separating them with a comma:

.card {
padding: 1.5em;
background: #fff;
$apply .rounded, .shadow;
}

Applying a mixin and a selector together

Jess supports mixing in both a selector AND a mixin when both have the same name and consist of simple selectors. (This is Less's default behavior, but optional in Jess syntax.)

To do so, use * before the mixin / selector name.

// Define a selector
.shadow {
box-shadow: 0 2px 8px #0002;
}

// Define a mixin with the same name
.shadow($color: #0002) {
box-shadow: 0 2px 8px $color;
outline: 1px solid $color;
}

// Apply/call both together:
.panel {
$ > *.shadow();
}

The CSS output is:

.shadow {
box-shadow: 0 2px 8px #0002;
}
.panel {
box-shadow: 0 2px 8px #0002;
outline: 1px solid #0002;
}

Anonymous mixins

Jess supports anonymous mixins, which are mixins that are not named. Anonymous mixins are started with @( or @{ and are followed by a block of rules.

$my-mixin: @() {
color: red;
}
.box {
$my-mixin();
}

Anonymous mixins without parameters can be written as:

$my-mixin: @{
color: red;
}
.box {
$my-mixin();
}

Passing sets of rules to mixins

Like Sass, Jess supports passing sets of rules to mixins by "assigning" an anonymous mixin callback to the mixin call. The mixin can then render rules using the built-in $content() mixin.

my-mixin() {
$content();
}
.box {
$ > my-mixin(): @{
color: red;
}
}

Passing sets of rules to mixins with parameters

You can also assign mixin callbacks to the mixin call.

media(...$types) {
$for ($type of $types) {
@media $(type) {
$content($type);
}
}
}

$ > media(screen, print): @($type) {
h1 {
font-size: 40px;
$if ($type = print) {
font-family: Calluna;
}
}
}

Calling mixins from other languages

Jess can call mixins from Less and Sass-compatible sources when those files are composed/imported into your Jess stylesheet.

Let's say we had these two files:

Less

// mixins.less
.less-mixin() {
color: blue;
}

Sass+

// mixins.scss
@mixin sass-mixin() {
color: green;
}

You could call them from your Jess stylesheet like this:

@-compose './mixins.scss' as *;
@-compose './mixins.less' as *;

.box {
$ > sass-mixin();
$ > .less-mixin();
}

This would output:

.box {
color: green;
color: blue;
}