HTML & CSS

@extendを使ってセレクタ単位でスタイルを共通化する

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

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

この記事のポイント

  • @extendはセレクタ単位でスタイルを共通化して、スタイル記述の重複を抑えることができる
  • 拡張専用セレクタ(プレースホルダーセレクタ)は不要なCSSの記述を減らすことができる
  • メディアクエリなどで@mediaを使うときはスコープに注意

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

同じスタイルを複数の箇所で適用したいとき、Sassの@extendを使うとスタイルを共通化することができます。
@extendセレクタ単位でスタイルを共通化します。

まずは@extendの基本的な使い方を見ていきます。
なお、当ページでは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;
  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;
}

Sassでは@extendを使って以下のように記述することができます。

style.scss

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

// 共通化したいスタイルを定義する
%fontSet {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.0rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

main {
  h1 {
    @extend %fontSet;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend %fontSet;
    font-size: 0.875rem;
  }
}

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

style.css

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

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

main h1 {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0083d9;
}

main p {
  font-size: 0.875rem;
}

上記の例では共通化するスタイルを「拡張専用セレクタ」と呼ぶ「%」から始まるセレクタを使って%fontSetを定義し、h1要素p要素に適用しています。
さらに個別の文字サイズや色などは必要に応じて個別に指定し、@extendで定義したスタイルを上書きする形になっています。

拡張専用セレクタを使わずにクラスセレクタで書くと以下のようになります。

style.scss

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

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

main {

  h1 {
    @extend .fontSet;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend .fontSet;
    font-size: 0.875rem;
  }
}

@extendで使用できるセレクタ

@extendで使うことができるセレクタの種類は以下の8種類です。

@extendで使用できるセレクタ

セレクタ
タイプセレクタh1 {...}、p {...}など
IDセレクタ#content1 {...}など
クラスセレクタ.content1 {...}、.btn {...}など
属性セレクタinput[type="text"] {...}など
連結セレクタ#content1.textare {...}、.btn.btn_orange {...}など
擬似クラスli:first-child {...}、a:hover {...}など
擬似要素li::before {...}、li::after {...}など
拡張専用セレクタ(プレースホルダーセレクタ)%btn {...}、%fontSet {...}など

拡張専用セレクタ(プレースホルダーセレクタ)

@extendではCSSと同様のセレクタに加えて、拡張専用セレクタ(プレースホルダーセレクタ)を使うことができます。
Sassをコンパイルしたときに、CSSと同じセレクタは@extendする前の元々あるセレクタと適用するセレクタを両方記述しますが、拡張セレクタは適用するセレクタに置き換わります。

style.scss

// クラスセレクタ
.title {
  color: #0083d9;
}

// 拡張専用セレクタ(プレースホルダーセレクタ)
%blueColor {
  color: #0083d9;
}

#content1 {
  h1 {
    @extend .title;
    @extend %blueColor;
  }
}

コンパイルして出力したCSSは以下のようになります。
Sassに書いた%blueColor自体はCSSファイルに書き出されないことが分かります。

style.css

// クラスセレクタ
.title, #content1 h1 {
  color: #0083d9;
}

// 拡張専用セレクタ(プレースホルダーセレクタ)
#content1 h1 {
  color: #0083d9;
}

この性質をもう少し掘り下げると、どこにも適用されない拡張専用セレクタはCSSに書き出されません。

style.scss

%mgb5 {
  margin-bottom: 5px;
}

%mgb10 {
  margin-bottom: 10px;
}

%mgb15 {
  margin-bottom: 15px;
}


#content1 {
  @extend %mgb10;
}

コンパイルして出力したCSSを確認すると、どこにも適用しない%mgb5%mgb15は出力されていません。
不要なセレクタやスタイルの記述を抑えることができます。

style.css

#content1 {
  margin-bottom: 10px;
}

@extendのスコープ

@extendはスコープがあり、ネストしているときは内側と外側でスコープが異なります。
以下の例では%fontSet1%fontSet2の2つスタイルのまとまりを定義し、#content1#content2でそれぞれ使用する例です。

style.scss

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

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

#content1 {

  %fontSet2 {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 1.0rem;
    font-weight: 500;
    line-height: 1.4em;
    color: #c00;
  }

  h1 {
    @extend %fontSet1;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend %fontSet2;
    font-size: 0.875rem;
  }
}

#content2 {

  h1 {
    @extend %fontSet1;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend %fontSet2;
    font-size: 0.875rem;
  }
}

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

style.css

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

// %fontSet1を展開
#content1 h1, #content2 h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.0rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

// %fontSet2を展開
#content1 p {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 1.0rem;
  font-weight: 500;
  line-height: 1.4em;
  color: #c00;
}

#content1 h1 {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0083d9;
}

#content1 p {
  font-size: 0.875rem;
}

#content2 h1 {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0083d9;
}

#content2 p {
  font-size: 0.875rem;
}

%fontSet1%fontSet2を展開したセレクタを確認すると、%fontSet1#content1#content2の両方で展開できていますが、%fontSet2#content1のみに展開されていることが確認できます。

もしネスト内で定義したスタイルを別の場所からも使用したいときは、セレクタをネストの外側に出すことができる@at-rootをセレクタの前に追記します。
以下の例は%fontSet2の前に@at-rootを追記して、#content2からも使用できるようにしています。

style.scss

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

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

#content1 {

  @at-root %fontSet2 {
    font-family: Arial, Helvetica, sans-serif;
    font-size: 1.0rem;
    font-weight: 500;
    line-height: 1.4em;
    color: #c00;
  }

  h1 {
    @extend %fontSet1;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend %fontSet2;
    font-size: 0.875rem;
  }
}

#content2 {

  h1 {
    @extend %fontSet1;
    font-size: 1.25rem;
    font-weight: 700;
    color: #0083d9;
  }

  p {
    @extend %fontSet2;
    font-size: 0.875rem;
  }
}

出力したCSSを見ると、今回は#content2p要素%fontSet2が適用されていることが確認できます。

style.css

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

#content1 h1, #content2 h1 {
  font-family: noto-sans-cjk-jp, sans-serif;
  font-size: 1.0rem;
  font-weight: 400;
  line-height: 1.8em;
  color: #000;
}

// %fontSet2を展開
#content1 p, #content2 p {
  font-family: Arial, Helvetica, sans-serif;
  font-size: 1.0rem;
  font-weight: 500;
  line-height: 1.4em;
  color: #c00;
}

#content1 h1 {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0083d9;
}

#content1 p {
  font-size: 0.875rem;
}

#content2 h1 {
  font-size: 1.25rem;
  font-weight: 700;
  color: #0083d9;
}

#content2 p {
  font-size: 0.875rem;
}

@mediaを使うときのスコープ

@extendのスコープでは@mediaの外で定義したスタイルを@media内で使うことはできません。
@media内でもスタイルの共通化をしたいときは@media内で改めて定義し直すことで使うことができます。

style.scss

// 共通化したいスタイルを定義する
%bgColor {
  background: #f7f7f7;
}

body {
  @extend %bgColor;
}

@media screen and (max-width: 980px) {

  // @media内用の共通化したいスタイルを定義する
  %bgColor2 {
    background: #eee;
  }

  body {
    @extend %bgColor2; // @mediaの外で定義した%bgColorは使えない
  }
}

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

style.css

// %bgColorを展開
body {
  background: #f7f7f7;
}

// %bgColor2を展開
@media screen and (max-width: 980px) {
  body {
    background: #eee;
  }
}

同じ条件の@mediaであれば、コード上の記述では{...}による囲みが異なっていても、定義したスタイルは共通化して使うことができます。
一方で、条件が異なる場合は別のスコープになるため、それぞれのスコープで定義する必要があります。

以下の例では2つの「screen and (max-width: 980px)」と、もう1つ別の「screen and (max-width: 768px)」から同じスタイルを使おうとしたときの例です。

style.scss

@media screen and (max-width: 980px) {

  // @media内用の共通化したいスタイルを定義する
  %bgColor2 {
    background: #eee;
  }
}

@media screen and (max-width: 980px) {

  body {
    @extend %bgColor2; // OK
  }
}

@media screen and (max-width: 768px) {

  body {
    @extend %bgColor2; // エラー
  }
}

こちらのscssファイルはコンパイルすると、「Error: You may not @extend an outer selector from within @media.」とエラーが出てしまいます。
正常にコンパイルするには、条件の異なる@mediaごとにスタイルを定義するか、@mixinを使います。

記事一覧

関連記事