HTML & CSS

@mixinを使ってCSSプロパティ単位でスタイルを共通化する

  1. 最終更新日:
  2. 公開日:

スタイルのまとまりを共通化するときに便利な@mixinについて、主な機能と使い方を解説します。

この記事のポイント

  • @mixinはCSSプロパティ単位でスタイルを共通化する
  • 引数を使うとCSSプロパティの値を柔軟に指定できる
  • @contentを使うとメディアクエリや:hoverの記述も共通化できる

スタイルのまとまりを共通化する@mixin

フォントやボタンなどスタイルを共通化して複数のパーツに適用したいとき、Sassでは@mixinを使うことができます。
@mixinCSSプロパティ単位でスタイルを共通化します。

まずは@mixinの基本的な使い方を見ていきます。
なお、当ページではSassはSCSS記法を使用します。

今回は例として、以下のHTMLにあるh1要素p要素に対してsytle.cssのスタイルを適用することを想定して進めます。(※CSSの書き方はあえて冗長になっています)

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>@extendと@mixinの違い</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <main>
    <h1>@extendと@mixinの違い</h1>
    <p>@extendと@mixinの違いを探るべく、我々はSassの奥地へと足を踏み入れた...</p>
  </main>
</body>
</html>

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  color: #000;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  color: #000;
}

このCSSをSassの@mixinでは以下のように書いて共通化することができます。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet {
  font-family: noto-sans-cjk-jp, sans-serif;
  color: #000;
}

main {
  h1 {
    @include fontSet; // @mixin fontSetを呼び出す
  }

  p {
    @include fontSet; // @mixin fontSetを呼び出す
  }
}

コンパイルして出力したCSSは以下のようになります。
の箇所が@mixinで展開したスタイルです。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  color: #000;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  color: #000;
}

この例では全く同じスタイルを共通化していますが、実際には見出しとテキストは文字サイズや色など異なることが多いと思います。
@mixinでは要素ごとに異なるプロパティの値を柔軟に変更することができる機能が備わっています。
以降でより詳しく解説していきます。

また、Sassではスタイルを共通化するためにもう1つ@extendが用意されています。
@mixinはCSSプロパティ単位でスタイルを共通化しますが、@extendセレクタ単位でスタイルを共通化することができます。
2つの機能の違いや使い分けについては、「@extendと@mixinの違い」で解説しています。

引数と初期値

@mixinは関数のように引数を使ってプロパティの値を指定することができます。
例えば以下のように、h1要素p要素に異なるfont-sizeプロパティfont-weightプロパティcolorプロパティを適用したいとします。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.8em;
  color: #0083d9;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

@mixinの引数を使うと、以下のように書くことができます。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000) {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: $font_size;
  font-weight: $font_weight;
  line-height: 1.8em;
  color: $color;
}

main {
  h1 {
    @include fontSet( 1.25rem, 700, #0083d9);
  }

  p {
    @include fontSet( 0.875rem);
  }
}

コンパイルすると@includeで呼び出すときに渡した値でスタイルが展開されていることを確認できます。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.8em;
  color: #0083d9;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

今回は定義している「@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000)」の「fontSet()」の中で、3つの引数を設定しています。
1つ目の「$font_size: 1.0rem」は変数$font_sizeと初期値1.0remのセットになり、もし@includeで呼び出すときに値が渡されなかったときは自動的に初期値が適用されます。

初期値は設定しないことも可能です。
その場合は@includeで呼び出すときに値が渡されなかったらコンパイル時でエラーを発生します。

特定の引数だけに値を渡す

@includeは先頭から順に@mixinの引数に値を渡していきますが、「第3引数だけ指定したい」といったケースもあります。
そのときは、@includeに「変数名:値」で値を渡すと、特定の引数にだけ値を渡すことができます。

以下の例では、先ほどの@mixinに文字色の$colorのみ指定しています。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000) {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: $font_size;
  font-weight: $font_weight;
  line-height: 1.8em;
  color: $color;
}

main {
  p {
    @include fontSet( $color: #c00);
  }
}

コンパイルすると、以下のCSSが出力されます。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #c00;
}

注意点として、@mixinの引数と@includeで渡す変数名が一致している必要があります。
この機能を使うと、第1引数と第3引数だけ指定するといったことも可能になり、スタイルの共通化をより柔軟に実現することができます。

1つの変数から複数の引数を指定する

@mixinに引数が複数あるとき、@includeから呼び出すときに1つの変数で複数の値を一括指定することができます。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000) {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: $font_size;
  font-weight: $font_weight;
  line-height: 1.8em;
  color: $color;
}

// ミックスインに渡す値を変数にセット
$h1_fontStyle: 1.25rem, 700, #0083d9;
$p_fontStyle: 0.875rem;

main {
  h1 {
    @include fontSet($h1_fontStyle...);
  }

  p {
    @include fontSet($p_fontStyle...);
  }
}

コンパイルして出力したCSSは以下のようになります。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.8em;
  color: #0083d9;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

@incliudeで引数の値を指定するときに、変数の後ろに「」を付けて値を渡します。

引数にコンマを含む値を渡す

CSSのプロパティの中にはtext-shadowプロパティやbackgroundプロパティなど、値に「,」が入るプロパティがあります。
このようなケースでは@mixinの引数の後ろに「」を付けて値を渡します。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000, $text_shadow...) {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: $font_size;
  font-weight: $font_weight;
  line-height: 1.8em;
  color: $color;
  text-shadow: $text_shadow;
}

main {
  h1 {
    @include fontSet( 1.25rem, 700, #0083d9, 1px 1px 0 #fff, 0 0 10px #64a7d2);
  }

  p {
    @include fontSet( 0.875rem);
  }
}

コンパイルして出力したCSSは以下のようになります。

style.css

body {
  font-size: 16px;
  background: #f7f7f7;
}

main h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.8em;
  color: #0083d9;
  text-shadow: 1px 1px 0 #fff, 0 0 10px #64a7d2;
}

main p {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 0.875rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

@mixinの引数の後ろに「」を付けると、変数を配列(List)として受け取ることができます。
ただし、「」を付ける引数の後ろは別の引数を設定できないため、必ず末尾の引数にする必要があります。
従って、文法上は1つの@mixinに設定できる「」付きの引数は1つのみとなります。

スコープ

@mixinはスコープがあります。
ネストの内側で@mixinを定義している場合、同じネスト内で同じ階層以下の場合のみ@includeで呼び出すことができます。
スコープ外の@mixinを呼び出そうとするとコンパイル時にエラーが発生します。

以下の例ではfontSetfontSet2という2つの@mixinを定義して、数箇所から@includeで呼び出している例です。

style.scss

body {
  font-size: 16px;
  background: #f7f7f7;
}

// ミックスインを定義
@mixin fontSet( $font_size: 1.0rem, $font_weight: 400, $color: #000) {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: $font_size;
  font-weight: $font_weight;
  line-height: 1.8em;
  color: $color;
}

#content1 {

  // 2つ目のミックスインを定義
  @mixin fontSet2 {
    color: #00568f;
  }

  p {
    @include fontSet(0.875rem); // OK
    @include fontSet2; // OK
  }

  ul {
    li {
      @include fontSet(0.875rem); // OK
      @include fontSet2; // OK
    }
  }
}

#content1 {

  h1 {
    @include fontSet(0.875rem); // OK
    @include fontSet2; // エラー
  }
}

#content2 {

  p {
    @include fontSet(0.875rem); // OK
    @include fontSet2; // エラー
  }
}

1つ目のfontSetはどこからでも@includeして呼び出すことができます。

2つ目のfontSet2は限定的なスコープになります。
#content2id属性が異なるため@includeで呼び出すことはできません。
また、同じ「#content1」であっても、{...}が異なる括りになっている場合はスコープ範囲外になり、こちらもエラーが発生します。

@contentを使って記述を共通化する

@mixinはさらに@contentという非常に便利な機能があります。
この機能はリンクの:hoverやメディアクエリなどCSSプロパティ以外の記述を共通化することができます。

以下の例ではリンクでホバー時によく使うCSSの擬似クラス「:hover」を@mixinで定義して共通化します。

style.scss

// ミックスインを定義(1)
@mixin linkSet( $color: #0083d9, $property: opacity) {
  color: $color;
  text-decoration: none;
  transition-property: $property;
  transition-duration: 0.3s;
  transition-timing-function: ease;
}

// ミックスインを定義(2)
@mixin buttonSet( $color: #fff, $bg_color: #0083d9, $property: opacity) {
  display: inline-block;
  padding: 10px 20px;
  color: $color;
  border-radius: 10px;
  background-color: $bg_color;
  text-decoration: none;
  transition-property: $property;
  transition-duration: 0.3s;
  transition-timing-function: ease;
}

// ミックスインを定義(3)
@mixin hoverSet {
  &:hover {
    @content;
  }
}

a {
  @include linkSet;
  @include hoverSet {
    opacity: 0.7;
  };
}

input[type=submit] {
  @include buttonSet;
  @include hoverSet {
    opacity: 0.7;
  };
}

コンパイルして出力したCSSは以下のようになります。

style.css

a {
  color: #0083d9;
  text-decoration: none;
  transition-property: opacity;
  transition-duration: 0.3s;
  transition-timing-function: ease;
}

a:hover {
  opacity: 0.7;
}

input[type=submit] {
  display: inline-block;
  padding: 10px 20px;
  color: #fff;
  border-radius: 10px;
  background-color: #0083d9;
  text-decoration: none;
  transition-property: opacity;
  transition-duration: 0.3s;
  transition-timing-function: ease;
}

input[type=submit]:hover {
  opacity: 0.7;
}

@mixinで定義しているhoverSetについて解説します。

セレクタにある「&:hover」の「&」はネストしている親のセレクタが入ります。
ここではhoverSetを「a」と「input[type=submit]」のネスト内で@includeしているため、「&」はそれぞれ「a」と「input[type=submit]」に置き換えられて「a:hover」と「input[type=submit]:hover」となります。

続いて、「&:hover {}」内にある@content@includeで呼び出すときに指定する{}の記述に置き換えられます。
今回は「a」と「input[type=submit]」はどちらも{}内に「opacity: 0.7;」と記述していますが、コンパイルすると@contentがこの記述に置き換えられます。

@contentを使ってメディアクエリの記述を共通化する

@contentはメディアクエリの記述を共通化することもできます。
メディアクエリでは最大表示幅などをpx単位で指定することが多いと思いますが、

以下の例ではディアクエリのブレークポイントを980px768pxであることを想定し、「#content1」と「#content2」にそれぞれ展開します。

style.scss

// メディアクエリの記述を定義する
@mixin mediaQuerySet($max_width: 980px) {

  @media screen and (max-width: $max_width) {
    @content;
  }
}

#content1 {
  width: 100%;
  max-width: 1200px;

  @include mediaQuerySet {
    padding: 0 3%;
    width: 94%;
  }

  @include mediaQuerySet(768px) {
    padding: 0 5%;
    width: 90%;
  }
}

#content2 {
  width: 100%;
  max-width: 1200px;

  @include mediaQuerySet {
    padding: 0 3%;
    width: 94%;
  }

  @include mediaQuerySet(768px) {
    padding: 0 5%;
    width: 90%;
  }
}

コンパイルして出力したCSSは以下のようになります。

style.css

#content1 {
  width: 100%;
  max-width: 1200px;
}

@media screen and (max-width: 980px) {
  #content1 {
    padding: 0 3%;
    width: 94%;
  }
}

@media screen and (max-width: 768px) {
  #content1 {
    padding: 0 5%;
    width: 90%;
  }
}

#content2 {
  width: 100%;
  max-width: 1200px;
}

@media screen and (max-width: 980px) {
  #content2 {
    padding: 0 3%;
    width: 94%;
  }
}

@media screen and (max-width: 768px) {
  #content2 {
    padding: 0 5%;
    width: 90%;
  }
}

定義したmediaQuerySetに引数で渡す値を変えることでブレークポイントのバリエーションを増やしていくことができます。

記事一覧

関連記事