2018-09-01 22:35:20 +02:00
|
|
|
---
|
|
|
|
title: "@mixin and @include"
|
2018-11-30 00:43:18 +01:00
|
|
|
table_of_contents: true
|
2019-03-05 01:24:31 +01:00
|
|
|
introduction: >
|
|
|
|
Mixins allow you to define styles that can be re-used throughout your
|
|
|
|
stylesheet. They make it easy to avoid using non-semantic classes like
|
|
|
|
`.float-left`, and to distribute collections of styles in libraries.
|
2018-09-01 22:35:20 +02:00
|
|
|
---
|
|
|
|
|
|
|
|
Mixins are defined using the `@mixin` at-rule, which is written
|
|
|
|
`@mixin <name> { ... }` or `@mixin name(<arguments...>) { ... }`. A mixin's name
|
|
|
|
can be any Sass identifier, and it can contain any [statement][] other than
|
|
|
|
[top-level statements][]. They can be used to encapsulate styles that can be
|
|
|
|
dropped into a single [style rule][]; they can contain style rules of their own
|
|
|
|
that can be nested in other rules or included at the top level of the
|
|
|
|
stylesheet; or they can just serve to modify variables.
|
|
|
|
|
|
|
|
[statement]: ../syntax/structure#statements
|
|
|
|
[top-level statements]: ../syntax/structure#top-level-statements
|
|
|
|
[style rule]: ../style-rules
|
|
|
|
|
|
|
|
Mixins are included into the current context using the `@include` at-rule, which
|
|
|
|
is written `@include <name>` or `@include <name>(<arguments...>)`, with the name
|
|
|
|
of the mixin being included.
|
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin reset-list {
|
|
|
|
margin: 0;
|
|
|
|
padding: 0;
|
|
|
|
list-style: none;
|
|
|
|
}
|
|
|
|
|
|
|
|
@mixin horizontal-list {
|
|
|
|
@include reset-list;
|
|
|
|
|
|
|
|
li {
|
|
|
|
display: inline-block;
|
|
|
|
margin: {
|
|
|
|
left: -2px;
|
|
|
|
right: 2em;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
nav ul {
|
|
|
|
@include horizontal-list;
|
|
|
|
}
|
|
|
|
===
|
|
|
|
@mixin reset-list
|
|
|
|
margin: 0
|
|
|
|
padding: 0
|
|
|
|
list-style: none
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin horizontal-list
|
|
|
|
@include reset-list
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
li
|
|
|
|
display: inline-block
|
|
|
|
margin:
|
|
|
|
left: -2px
|
|
|
|
right: 2em
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
nav ul
|
|
|
|
@include horizontal-list
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
Mixin names, like all Sass identifiers, treat hyphens and underscores as
|
|
|
|
identical. This means that `reset-list` and `reset_list` both refer to the
|
|
|
|
same mixin. This is a historical holdover from the very early days of Sass,
|
|
|
|
when it *only* allowed underscores in identifier names. Once Sass added
|
|
|
|
support for hyphens to match CSS's syntax, the two were made equivalent to
|
|
|
|
make migration easier.
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
## Arguments
|
|
|
|
|
|
|
|
<%# When changing this section, don't forget to change the function arguments
|
|
|
|
section as well! %>
|
|
|
|
|
|
|
|
Mixins can also take arguments, which allows their behavior to be customized
|
|
|
|
each time they're called. The arguments are specified in the `@mixin` rule after
|
|
|
|
the mixin's name, as a list of variable names surrounded by parentheses. The
|
|
|
|
mixin must then be included with the same number of arguments in the form of
|
|
|
|
[SassScript expressions][]. The values of these expression are available within
|
|
|
|
the mixin's body as the corresponding variables.
|
|
|
|
|
|
|
|
[SassScript expressions]: ../syntax/structure#expressions
|
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin rtl($property, $ltr-value, $rtl-value) {
|
|
|
|
#{$property}: $ltr-value;
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
[dir=rtl] & {
|
|
|
|
#{$property}: $rtl-value;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.sidebar {
|
|
|
|
@include rtl(float, left, right);
|
|
|
|
}
|
|
|
|
===
|
|
|
|
@mixin rtl($property, $ltr-value, $rtl-value)
|
|
|
|
#{$property}: $ltr-value
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
[dir=rtl] &
|
|
|
|
#{$property}: $rtl-value
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.sidebar
|
|
|
|
@include rtl(float, left, right)
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
Argument lists can also have trailing commas! This can makes it easier to
|
|
|
|
avoid syntax errors when refactoring your stylesheets.
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
### Optional Arguments
|
|
|
|
|
|
|
|
Normally, every argument a mixin declares must be passed when that mixin is
|
|
|
|
included. However, you can make an argument optional by defining a *default
|
|
|
|
value* which will be used if that arguments isn't passed. Default values use the
|
|
|
|
same syntax as [variable declarations][]: the variable name, followed by a colon
|
|
|
|
and a [SassScript expression][]. This makes it easy to define flexible mixin
|
|
|
|
APIs that can be used in simple or complex ways.
|
|
|
|
|
|
|
|
[variable declarations]: ../variables
|
|
|
|
[SassScript expression]: ../syntax/structure#expressions
|
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin replace-text($image, $x: 50%, $y: 50%) {
|
|
|
|
text-indent: -99999em;
|
|
|
|
overflow: hidden;
|
|
|
|
text-align: left;
|
|
|
|
|
|
|
|
background: {
|
|
|
|
image: $image;
|
|
|
|
repeat: no-repeat;
|
|
|
|
position: $x $y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.mail-icon {
|
|
|
|
@include replace-text(url("/images/mail.svg"), 0);
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
2018-10-23 22:42:40 +02:00
|
|
|
===
|
|
|
|
@mixin replace-text($image, $x: 50%, $y: 50%)
|
|
|
|
text-indent: -99999em
|
|
|
|
overflow: hidden
|
|
|
|
text-align: left
|
|
|
|
|
|
|
|
background:
|
|
|
|
image: $image
|
|
|
|
repeat: no-repeat
|
|
|
|
position: $x $y
|
|
|
|
|
|
|
|
.mail-icon
|
|
|
|
@include replace-text(url("/images/mail.svg"), 0)
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
Default values can be any SassScript expression, and they can even refer to
|
|
|
|
earlier arguments!
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
### Keyword Arguments
|
|
|
|
|
|
|
|
When a mixin is included, arguments can be passed by name in addition to passing
|
|
|
|
them by their position in the argument list. This is especially useful for
|
|
|
|
mixins with multiple optional arguments, or with [boolean][] arguments whose
|
|
|
|
meanings aren't obvious without a name to go with them. Keyword arguments use
|
|
|
|
the same syntax as [variable declarations][] and [optional arguments][].
|
|
|
|
|
|
|
|
[boolean]: ../values/booleans
|
|
|
|
[optional arguments]: #optional-arguments
|
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin square($size, $radius: 0) {
|
|
|
|
width: $size;
|
|
|
|
height: $size;
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@if $radius != 0 {
|
|
|
|
border-radius: $radius;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.avatar {
|
|
|
|
@include square(100px, $radius: 4px);
|
|
|
|
}
|
|
|
|
===
|
|
|
|
@mixin square($size, $radius: 0)
|
|
|
|
width: $size
|
|
|
|
height: $size
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@if $radius != 0
|
|
|
|
border-radius: $radius
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.avatar
|
|
|
|
@include square(100px, $radius: 4px)
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% heads_up do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
Because *any* argument can be passed by name, be careful when renaming a
|
|
|
|
mixin's arguments... it might break your users! It can be helpful to keep the
|
|
|
|
old name around as an [optional argument][] for a while and printing a
|
|
|
|
[warning][] if anyone passes it, so they know to migrate to the new argument.
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
[optional argument]: #optional-arguments
|
|
|
|
[warning]: warn
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
### Taking Arbitrary Arguments
|
|
|
|
|
|
|
|
Sometimes it's useful for a mixin to be able to take any number of arguments. If
|
|
|
|
the last argument in a `@mixin` declaration ends in `...`, then all extra
|
|
|
|
arguments to that mixin are passed to that argument as a [list][]. This argument
|
|
|
|
is known as an [argument list][].
|
|
|
|
|
|
|
|
[list]: ../values/lists
|
2018-10-23 22:01:12 +02:00
|
|
|
[argument list]: ../values/lists#argument-lists
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin order($height, $selectors...) {
|
|
|
|
@for $i from 0 to length($selectors) {
|
|
|
|
#{nth($selectors, $i + 1)} {
|
|
|
|
position: absolute;
|
|
|
|
height: $height;
|
|
|
|
margin-top: $i * $height;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@include order(150px, "input.name", "input.address", "input.zip");
|
|
|
|
===
|
|
|
|
@mixin order($height, $selectors...)
|
|
|
|
@for $i from 0 to length($selectors)
|
|
|
|
#{nth($selectors, $i + 1)}
|
|
|
|
position: absolute
|
|
|
|
height: $height
|
|
|
|
margin-top: $i * $height
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@include order(150px, "input.name", "input.address", "input.zip")
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
#### Taking Arbitrary Keyword Arguments
|
|
|
|
|
|
|
|
Argument lists can also be used to take arbitrary keyword arguments. The
|
|
|
|
[`keywords()` function][] takes an argument list and returns any extra keywords
|
|
|
|
that were passed to the mixin as a [map][] from argument names (not including
|
|
|
|
`$`) to those arguments' values.
|
|
|
|
|
2019-01-09 23:14:29 +01:00
|
|
|
[`keywords()` function]: ../functions/map#keywords
|
2018-09-01 22:35:20 +02:00
|
|
|
[map]: ../values/maps
|
|
|
|
|
|
|
|
<%# TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass %>
|
|
|
|
|
2018-10-23 22:01:12 +02:00
|
|
|
<%= partial 'code-snippets/example-mixin-arbitrary-keyword-arguments' %>
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
If you don't ever pass an argument list to the [`keywords()` function][], that
|
|
|
|
argument list won't allow extra keyword arguments. This helps callers of your
|
|
|
|
mixin make sure they haven't accidentally written any typos in their argument
|
|
|
|
names.
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2019-01-09 23:14:29 +01:00
|
|
|
[`keywords()` function]: ../functions/map#keywords
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
#### Passing Arbitrary Arguments
|
|
|
|
|
|
|
|
Just like argument lists allow mixins to take arbitrary positional or keyword
|
|
|
|
arguments, the same syntax can be used to *pass* positional and keyword
|
|
|
|
arguments to a mixin. If you pass a list followed by `...` as the last argument
|
|
|
|
of an include, its elements will be treated as additional positional arguments.
|
|
|
|
Similarly, a map followed by `...` will be treated as additional keyword
|
|
|
|
arguments. You can even pass both at once!
|
|
|
|
|
|
|
|
<% example(autogen_css: false) do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
$form-selectors: "input.name", "input.address", "input.zip" !default;
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@include order(150px, $form-selectors...);
|
|
|
|
===
|
|
|
|
$form-selectors: "input.name", "input.address", "input.zip" !default
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
@include order(150px, $form-selectors...)
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
Because an [argument list][] keeps track of both positional and keyword
|
|
|
|
arguments, you use it to pass both at once to another mixin. That makes it
|
|
|
|
super easy to define an alias for a mixin!
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
[argument list]: ../values/lists#argument-lists
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
<% example do %>
|
|
|
|
@mixin btn($args...) {
|
|
|
|
@warn "The btn() mixin is deprecated. Include button() instead.";
|
|
|
|
@include button($args...);
|
|
|
|
}
|
|
|
|
===
|
|
|
|
@mixin btn($args...)
|
|
|
|
@warn "The btn() mixin is deprecated. Include button() instead."
|
|
|
|
@include button($args...)
|
|
|
|
<% end %>
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
## Content Blocks
|
|
|
|
|
|
|
|
In addition to taking arguments, a mixin can take an entire block of styles,
|
|
|
|
known as a *content block*. A mixin can declare that it takes a content block by
|
|
|
|
including the `@content` at-rule in its body. The content block is passed in
|
|
|
|
using curly braces like any other block in Sass, and it's injected in place of
|
|
|
|
the `@content` rule.
|
|
|
|
|
|
|
|
<% example do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
@mixin hover {
|
|
|
|
&:not([disabled]):hover {
|
|
|
|
@content;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.button {
|
|
|
|
border: 1px solid black;
|
|
|
|
@include hover {
|
|
|
|
border-width: 2px;
|
|
|
|
}
|
2018-09-01 22:35:20 +02:00
|
|
|
}
|
2018-10-23 22:42:40 +02:00
|
|
|
===
|
|
|
|
@mixin hover
|
|
|
|
&:not([disabled]):hover
|
|
|
|
@content
|
2018-09-01 22:35:20 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-10-23 22:42:40 +02:00
|
|
|
.button
|
|
|
|
border: 1px solid black
|
|
|
|
@include hover
|
|
|
|
border-width: 2px
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% fun_fact do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
A mixin can include multiple `@content` at-rules. If it does, the content
|
|
|
|
block will be included separately for each `@content`.
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<% heads_up do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
A content block is *lexically scoped*, which means it can only see [local
|
|
|
|
variables][] in the scope where the mixin is included. It can't see any
|
|
|
|
variables that are defined in the mixin it's passed to, even if they're
|
|
|
|
defined before the content block is invoked.
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2019-01-09 23:14:29 +01:00
|
|
|
[local variables]: ../variables#scope
|
|
|
|
<% end %>
|
2018-09-01 22:35:20 +02:00
|
|
|
|
2018-11-06 02:53:30 +01:00
|
|
|
### Passing Arguments to Content Blocks
|
|
|
|
|
2019-03-09 01:48:27 +01:00
|
|
|
<% impl_status dart: '1.15.0', libsass: false, ruby: false %>
|
2018-11-06 02:53:30 +01:00
|
|
|
|
|
|
|
A mixin can pass arguments to its content block the same way it would pass
|
|
|
|
arguments to another mixin by writing `@content(<arguments...>)`. The user
|
|
|
|
writing the content block can accept arguments by writing
|
|
|
|
`@include <name> using (<arguments...>)`. The argument list for a content block
|
|
|
|
works just like a mixin's argument list, and the arguments passed to it by
|
|
|
|
`@content` work just like passing arguments to a mixin.
|
|
|
|
|
|
|
|
<% heads_up do %>
|
|
|
|
If a mixin passes arguments to its content block, that content block *must*
|
|
|
|
declare that it accepts those arguments. This means that it's a good idea to
|
|
|
|
only pass arguments by position (rather than by name), and it means that
|
|
|
|
passing more arguments is a breaking change.
|
|
|
|
|
|
|
|
If you want to be flexible in what information you pass to a content block,
|
|
|
|
consider passing it a [map][] that contains information it may need!
|
|
|
|
|
|
|
|
[map]: ../values/maps
|
|
|
|
<% end %>
|
|
|
|
|
|
|
|
<%# TODO(nweiz): auto-generate this CSS once we're compiling with Dart Sass %>
|
|
|
|
|
|
|
|
<% example(autogen_css: false) do %>
|
|
|
|
@mixin media($types...) {
|
|
|
|
@each $type in $types {
|
|
|
|
@media #{$type} {
|
|
|
|
@content($type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@include media(screen, print) using ($type) {
|
|
|
|
h1 {
|
|
|
|
font-size: 40px;
|
|
|
|
@if $type == print {
|
|
|
|
font-family: Calluna;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
===
|
|
|
|
@mixin media($types...)
|
|
|
|
@each $type in $types
|
|
|
|
@media #{$type}
|
|
|
|
@content($type)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@include media(screen, print) using ($type)
|
|
|
|
h1
|
|
|
|
font-size: 40px
|
|
|
|
@if $type == print
|
|
|
|
font-family: Calluna
|
|
|
|
===
|
|
|
|
@media screen {
|
|
|
|
h1 {
|
|
|
|
font-size: 40px;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@media print {
|
|
|
|
h1 {
|
|
|
|
font-size: 40px;
|
|
|
|
font-family: Calluna;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
<% end %>
|
|
|
|
|
2018-09-01 22:35:20 +02:00
|
|
|
## Indented Mixin Syntax
|
|
|
|
|
|
|
|
The [indented syntax][] has a special syntax for defining and using mixins, in
|
|
|
|
addition to the standard `@mixin` and `@include`. Mixins are defined using the
|
|
|
|
character `=`, and they're included using `+`. Although this syntax is terser,
|
|
|
|
it's also harder to understand at a glance and users are encouraged to avoid it.
|
|
|
|
|
|
|
|
[indented syntax]: ../syntax#the-indented-syntax
|
|
|
|
|
|
|
|
<% example(syntax: :sass) do %>
|
2018-10-23 22:42:40 +02:00
|
|
|
=reset-list
|
|
|
|
margin: 0
|
|
|
|
padding: 0
|
|
|
|
list-style: none
|
|
|
|
|
|
|
|
=horizontal-list
|
|
|
|
+reset-list
|
|
|
|
|
|
|
|
li
|
|
|
|
display: inline-block
|
|
|
|
margin:
|
|
|
|
left: -2px
|
|
|
|
right: 2em
|
|
|
|
|
|
|
|
nav ul
|
|
|
|
+horizontal-list
|
2018-09-01 22:35:20 +02:00
|
|
|
<% end %>
|