他で定義されたスタイルを継承する @extend

メモ:  Category:front-end

@extend を使用すると、他で定義されたスタイルを継承して新たなスタイルを作ることができます。

スタイルを継承する

オブジェクト指向のプログラミング言語でよく聞かれる「継承」ですが、@extend を使うと Sass でも定義済みのスタイルを「継承」することができます。

例えば、次のように部品としてボタンのスタイルを定義したとします。しかし、ボタンは1種類ではなく役割によっていくつか増やしたい事があったりします。

.c-btn {
  display: inline-block;
  cursor: pointer;
  line-height: 1;
  overflow: hidden;
  text-align: center;
  text-decoration: none;
  vertical-align: middle;
  white-space: nowrap;
  background-color: #fff;
  border: 1px solid #cccccc;
  padding: 0.75rem 1.5rem;
  color: #333;
}

そんな時、定義済みの .c-btn と同じ定義を書いて新しいボタンの定義を作成するのではなく、次のように @extend を使用して .c-btn のスタイルを継承して変更したい部分だけを定義することができます。

.c-btn {
  display: inline-block;
  cursor: pointer;
  line-height: 1;
  overflow: hidden;
  text-align: center;
  text-decoration: none;
  vertical-align: middle;
  white-space: nowrap;
  background-color: #fff;
  border: 1px solid #cccccc;
  padding: 0.75rem 1.5rem;
  color: #333;
}

// .c-btn の定義を引き継ぎつつ .c-btn--ghost に設定したプロパティを上書きします。
.c-btn--ghost {
  @extend .c-btn
  background-color: transparent !important;
  border: 2px solid #4E342E;
  color: #4E342E;
}

コンパイルすると次のような CSS が出力されます。出力結果が少しだけ美しくない気がしますが、継承元のスタイルが引き継がれていることを確認することができます。

.c-btn, .c-btn--ghost {
  display: inline-block;
  cursor: pointer;
  line-height: 1;
  overflow: hidden;
  text-align: center;
  text-decoration: none;
  vertical-align: middle;
  white-space: nowrap;
  background-color: #fff;
  border: 1px solid #cccccc;
  padding: 0.75rem 1.5rem;
  color: #333; }

.c-btn--ghost {
  background-color: transparent !important;
  border: 2px solid #4E342E;
  color: #4E342E; }

CSS では上書きもできますし、 Sass では @mixin という機能もありますので積極的に使いたい機能ではないかもしれません。よい使い方を考えたいところです。

プレースホルダーセレクタ

プレースホルダーセレクタという仕組みを使うと、コンパイル時に出力されず、継承して使うことを目的にしたスタイルを定義することができます。次のように、「%」 で始まる名称のセレクタでスタイルを定義しておくと、その定義は継承されるための専用定義となります。

%btn{
  display: inline-block;
  cursor: pointer;
  line-height: 1;
  overflow: hidden;
  text-align: center;
  text-decoration: none;
  vertical-align: middle;
}

.btn-gray{
  @extend %btn;
  background-color: #999;
  color: #111;
}

.btn-red{
  @extend %btn;
  background-color: #e74c3c;
  color: #333;
}

コンパイルすると次のように %btn の部分は出力されず、 .btn-gray と .btn-red のみが出力されます。

.btn-gray, .btn-red {
  display: inline-block;
  cursor: pointer;
  line-height: 1;
  overflow: hidden;
  text-align: center;
  text-decoration: none;
  vertical-align: middle; }

.btn-gray {
  background-color: #999;
  color: #111; }

.btn-red {
  background-color: #e74c3c;
  color: #333; }

継承できないセレクタ(制約)

@extend では、継承可能なセレクタとそうでないセレクタがあり、次のセレクタは継承できないセレクタになります。

  • 子セレクタ 例: 「p > strong」、「.foo > .bar」
  • 子孫セレクタ 例: 「p strong」、「.foo .bar」
  • 隣接セレクタ 例: 「h2 + h3」、「.foo + .bar」
  • 間接セレクタ 例: 「h3 ~ h3」、「.foo ~ .bar」

また、メディアクエリでは機能が異なっており、メディアクエリーの中で定義されたスタイルのみ継承可能となります。(メディアクエリーの外側で定義されたスタイルは継承できません。)

エラーとなる例

.warning {
  font-weight: bolder;
  border: solid 3px #ff6600;
  color: #ff6600;
}

@media screen and (max-width: 600px) {
  .error {
    @extend .warning;
    border-color: #e74c3c;
    color: #e74c3c;
  }
}

コンパイル可能な例

@media screen and (max-width: 600px) {
  .warning {
    font-weight: bolder;
    border: solid 3px #ff6600;
    color: #ff6600;
  }

  .error {
    @extend .warning;
    border-color: #e74c3c;
    color: #e74c3c;
  }
}

bluenote by BBB