Sass の Mixin 機能は、ひとまとまりのスタイルを Mixin として定義 (@mixin
) しておき、複数の場所からそのスタイルをインクルード (@include
) して使用する仕組みです。
Mixin は、スタイルセットを提供する関数のようなものと考えるとわかりやすいです。
便利な Mixin を SCSS ファイルにまとめて定義しておけば、再利用可能な Sass ライブラリとして使用できます。
定義した Mixin は @include
して使用しない限り CSS に出力されることはないので、Sass ライブラリの中の Mixin 定義が増えても、最終的な CSS が肥大化する心配はありません。
下記の例では、round-box
という名前の Mixin を定義して、pre
要素と .ad-widget
クラスのスタイル定義からインクルードしています。
@mixin round-box {
border: solid 1px gray;
border-radius: 5px;
padding: 10px;
}
pre {
@include round-box;
line-height: 1.3;
}
.ad-widget {
@include round-box;
background: #ccc;
}
pre {
border: solid 1px gray;
border-radius: 5px;
padding: 10px;
line-height: 1.3;
}
.ad-widget {
border: solid 1px gray;
border-radius: 5px;
padding: 10px;
background: #ccc;
}
Mixin で定義したスタイルセットが、@include
した位置に展開されていることが分かります。
@extend
を使用した場合より非効率なコードになってしまいます。
このあたりは Sass のバージョンアップによって改善されるかもしれません。
とはいえ、Mixin には後述のような、引数を扱う仕組みなどがあり、@extend
より使用する機会は多いでしょう。
Sass のコードの中で &
は親セレクタを参照するために使用することができますが、Mixin の定義の中でも &
を使用することができます。
Mixin の定義のトップレベルで &
を参照すると、呼び出し元(@include
したところ)のセレクタに置き換えられます。
下記は、::after
疑似要素を使用した典型的な clearfix イディオムを、Mixin で定義する例です。
@mixin clearfix {
&::after {
content: '';
display: block;
clear: both;
}
}
.breadcrumb {
@include clearfix;
float: left;
margin: 10px;
}
.breadcrumb {
float: left;
margin: 10px;
}
.breadcrumb::after {
content: '';
display: block;
clear: both;
}
下記のように、入れ子スタイルで定義した Mixin は、ルートレベルで @include
して使用することができます。
@mixin pretty-link {
a {
font-weight: bolder;
text-decoration: none;
&:link { color: #36f; } // 未訪問
&:visited { color: #03c; } // 訪問済
&:hover { background: #def; } // マウスフォーカス
&:focus { background: #fdd; } // 選択中
}
}
// ルートレベルでインクルードできる
@include pretty-link;
a {
font-weight: bolder;
text-decoration: none;
}
a:link {
color: #36f;
}
a:visited {
color: #03c;
}
a:hover {
background: #def;
}
a:focus {
background: #fdd;
}
Mixin には任意の数の引数を渡すことができます。
下記の color-box
は、2つの引数 ($color
、$border-width
) を受け取るように定義しています。
@mixin color-box($color, $border-width) {
color: $color;
background: lighten($color, 45%);
border: solid $color $border-width;
padding: 0.5em;
}
.warn {
@include color-box(darkorange, 3px);
}
.error {
@include color-box(red, 3px);
}
.warn {
color: darkorange;
background: #fff4e6;
border: solid darkorange 3px;
padding: 0.5em;
}
.error {
color: red;
background: #ffe6e6;
border: solid red 3px;
padding: 0.5em;
}
Mixin の引数名の後ろに : デフォルト値
と記述することで、引数ごとのデフォルト値を設定しておくことができます。
@mixin color-box($color: black, $border-width: 1px) {
color: $color;
background: lighten($color, 45%);
border: solid $color $border-width;
padding: 0.5em;
}
.error {
@include color-box(red); // 2番目のパラメータのみ省略
}
.error {
color: red;
background: #ffe6e6;
border: solid red 1px;
padding: 0.5em;
}
すべてのパラメータを省略する場合は、下記のいずれの書き方でも OK です。
@include color-box();
@include color-box;
Mixin をインクルード (@include
) するとき、パラメータの値を名前付きで指定することができます。
名前付き引数として値を指定するときは、引数の指定順序は任意となります。
@include color-box($border-width: 3px, $color: red);
名前付き引数を使用すると記述量は若干増えますが、それぞれのパラメータが何を表しているか明確になるというメリットがあります。
また、名前付き引数を使用することで、任意の位置のデフォルト引数のみを省略することが可能になります。
次の例では、1番目の引数 $color
を省略し、2番目の引数 $border-width
のみを指定して Mixin をインクルードしています。
@include color-box($border-width: 3px);
Mixin の引数の末尾に ...
を付けると、その引数を可変長引数として扱うことができます。
例えば、CSS の box-shadow
プロパティはもともと可変長のパラメータを受け付けますが、このようなプロパティを扱う Mixin は次のように定義することができます。
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.box {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}
.box {
-moz-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
-webkit-box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
box-shadow: 0px 4px 5px #666, 2px 6px 10px #999;
}
上記のように、Mixin はベンダープレフィックスの必要なプロパティをまとめて定義するためにも利用することができます。
ちなみに、...
というキーワードは、リスト変数の値を展開して、複数のパラメータとして Mixin や関数に渡すためにも使用します。
@mixin fancy-border($color, $width, $style) {
border: $width $style $color;
border-radius: 5px;
}
$value-list: green, 1px, solid;
$value-map: (color: red, width: 3px, style: dashed);
.success {
@include fancy-border($value-list...);
}
.failure {
@include fancy-border($value-map...);
}
.success {
border: 1px solid green;
border-radius: 5px;
}
.failure {
border: 3px dashed red;
border-radius: 5px;
}
Mixin の定義の中で @content
を参照すると、呼び出し側から渡されたスタイル定義をそこに展開することができます。
呼び出し側では、次のような構文でスタイル定義のブロックを Mixin に渡します。
@include <Mixin名> {
スタイル定義
}
この仕組みは、同じ条件のメディアクエリを複数のスタイルに対して適用するときに便利です。
@mixin media-large {
// スクリーンの横幅が広い端末(PCなど)の場合
@media screen and (min-width: 800px) {
@content;
}
}
main {
margin: 1em; // 幅が狭いときはマージンは小さく
@include media-large {
margin: 2em; // 幅が広いときはマージンを大きく
}
}
#xMenu {
display: none; // 幅が狭いときはメニューは表示しない
@include media-large {
display: block; // 幅が広いときはメニューを表示する
width: 20rem;
}
}
main {
margin: 1em;
}
@media screen and (min-width: 800px) {
main {
margin: 2em;
}
}
#xMenu {
display: none;
}
@media screen and (min-width: 800px) {
#xMenu {
display: block;
width: 20rem;
}
}
スクリーンサイズのブレイクポイントとする横幅などは、下記のように変数に定義しておくとメンテナンスしやすいかもしれません。
$LARGE_SCREEN_WIDTH: 800px;
@mixin media-large {
@media screen and (min-width: $LARGE_SCREEN_WIDTH) {
@content;
}
}
ちなみに、Mixin の中で content-exists()
関数を使用すると、コンテントブロックが Mixin に渡されたかどうかを調べることができます。
変数名や関数名も同様ですが、Mixin の名前の中で使用したハイフン (-
) と、アンダースコア (_
) は相互に置き換えが可能になっています。
@mixin foo-bar {
color: red;
}
main {
@include foo_bar; // ハイフンをアンダースコアに変えても参照できる
}
ただし、この振る舞いは歴史的な理由によるものなので、ハイフンで定義したものはハイフンを使って参照すべきでしょう。
Sass の @extend ディレクティブも似たような機能を提供していますが、@extend
はあくまでセレクタを継承する機能であり、Mixin 機能より若干複雑な振る舞いをします。
Mixin 機能はスタイルそのものを使いまわすものであり、@extend
ディレクティブの振る舞いよりシンプルです(機能としては強力ですが)。
Mixin と @extend
の違いをざっとあげると下記のような感じでしょうか。
@extend
とは異なり、セレクタの組み合わせによるルールが継承されない。@extend
を使ったものよりも冗長になる可能性がある。Mixin は、ベンダープレフィックスが必要なプロパティをまとめて指定するためにも活用できます。
@mixin border-radius($radius: 5px) {
-moz-border-radius: $radius;
-webkit-border-radius: $radius;
border-radius: $radius;
}
.box {
@include border-radius(10px);
}
@mixin box-shadow($shadows...) {
-moz-box-shadow: $shadows;
-webkit-box-shadow: $shadows;
box-shadow: $shadows;
}
.box {
@include box-shadow(0px 4px 5px #666, 2px 6px 10px #999);
}