すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

HTML5・CSS3

【HTML5・CSS3】 Sassを攻略してコーディングを効率良く書こう!

ぴっかちゃん
ぴっかちゃん

Sassとは、Syntactically Awesome Style Sheetsの略語で、CSSを拡張したメタ言語のことを指します。

メタ言語とは、言語を定義する為の言語のことです。つまり、SassはCSSのスタイルシート言語の機能を拡張した言語のことです。

CSS | CSSの書き方 -->
1
2
3
4
5
6
7
8
ul {
  margin: 1em;
}

li {
    padding-left: 1em;
    color: red;
}

Sassの一種であるSCSS記法の場合は、下記のように記述します。

SCSS | SCSSの書き方
1
2
3
4
5
6
7
8
ul {
  margin: 1em;

  li {
  padding-left: 1em;
  color: red;
  }
}

Sassを使うことで、コードの記述量が減って保守性が高まるので、人為的なミスも減らす事が出来ます。

おすすめ

RailsのCSS設計についてもっと詳しく知りたい方は、こちらのPikawaka動画『RailsエンジニアのためのCSS設計の解説動画』もおすすめです!

Railsアプリケーションによく取り入れられる「FLOCSS・BEM・SCSS」を組み合わせたCSS設計方法を動画で解説しています。

Sassの基礎情報

この章では、Sassの基礎情報について解説します。

Sassを使った方が良い理由とは?

CSSでもコーディングを行うことが出来ますが、Sassを使った方が良い理由は主に下記の3つあります。

  1. コードを減らすことが出来る
  2. 変数や関数を使うことが出来るので、効率的にコードが書ける
  3. 余計なコードを減らす事で、ミスを減らすことが出来る

Sassを利用するとコードをシンプルにする事が出来るので、保守性が高まり人為的なミスを減らすことが出来ます。CSSと比較しながら、Sassの書き方を解説していきます。

Sassの種類とは?

Sassには「SASS記法」と「SCSS記法」の2種類あります。
それぞれコーディングのルールが違うので、書き方も変わってきます。

下記のCSSの書き方を例にして、SASS記法とSCSS記法で比較していきます。

CSS -->
1
2
3
4
5
6
7
8
9
/* CSSの書き方 */
ul {
  margin: 1em;
}

li {
    padding-left: 1em;
    color: red;
}

SASS記法

SASS記法は、元々SCSS記法が導入される以前にSassで書かれていた記法です。
SCSS記法やCSSで書くよりもコードが簡潔になるので記述量を更に減らすことが出来ます。

CSS -->
1
2
3
4
5
6
7
8
9
/* CSSの書き方 */
ul {
  margin: 1em;
}

li {
    padding-left: 1em;
    color: red;
}

SASS記法の場合は、下記のように記述します。

SASS記法 / ファイル名.sass
1
2
3
4
5
6
/* SASSの書き方 */
ul
  margin: 1em
  li
    padding-left: 1em
    color: red

CSSと比べると波括弧{}がなくなるのでシンプルなコードにする事が出来ました。書き方のポイントは、下記の3つあります。

ポイント
  1. 拡張子は.sassのファイル形式です
  2. スタイルブロックを区切る波括弧{}が無くなる代わりに入れ子はインデントを使用する
  3. 通常の値の終わりにあるセミコロンは省略することが出来る

SCSS記法

SCSS記法は、スタイルの書き方がCSSと変わらないので、元々CSSを書いていた方はSASS記法よりも入りやすいかと思います。Sass3.0から導入された記法です。

CSS -->
1
2
3
4
5
6
7
8
9
/* CSSの書き方 */
ul {
  margin: 1em;
}

li {
    padding-left: 1em;
    color: red;
}

SCSS記法の場合は、下記のように記述します。

SCSS記法 / ファイル名.scss
1
2
3
4
5
6
7
8
9
/* SCSSの書き方 */
ul {
  margin: 1em;

  li {
  padding-left: 1em;
  color: red;
  }
}

CSSと見た目は似ていますが、波括弧{}を入れ子にしている箇所が違います。ulの波括弧{}の中にliの子要素を入れ子構造にする事で、ulを親要素として認識します。

ポイント
  1. 拡張子は.scssのファイル形式です。
  2. SCSS記法は入れ子を主とした記法になる
  3. スタイルの書き方はCSSとほぼ変わらない
  4. 波括弧{}を使い入れ子構造にして、CSSの依存関係を示す

また、現在はSCSS記法の書き方が主流になっているので、ここからはSassの書き方はSCSS記法で説明します。

Sassを導入

Sassを使うには、Ruby本体とSass本体を導入する必要があります。また、導入後にはSassをCSSにコンパイルしなければ、スタイルを反映することが出来ません。

Sassの導入手順とコンパイルについては別記事で解説させて頂いて、今回は「 sassmeisterオンラインエディタ」を使ってSassのコードをCSSに変換された結果を確認していきます。

sassmeisterの使い方

sassmeisterは、リアルタイムでSass,ScssをCSSに変換してくれるサービスです。

これから解説するコードをsassmeisterに貼り付けてもらうとコードの実行結果をリアルタイムで確認する事が出来ます。

下記のリンクよりsassmeisterにアクセスして下さい。

sassmeister.com

アクセスしたら下記の様な画面が表示されます。

sassmeister初期画面

今回はHTMLも使うので、ViewのプルダウンからHTMLを選択します。

sassmeister設定

左のsassのエディタにコードを書くと、真ん中のエディタにCSSが変換されます。また、HTMLとSassのコードの結果は、下のviewに表示されます。

sassmeister構造

これから解説されるSassのコードをHTMLと共にsassmeisterで確認してみて下さい。整理しながらコードを理解する事が出来るのでおすすめです。

▼ Sassに関しては、この1冊あれば大丈夫!
基礎から応用的な使い方まで網羅する良書です。HTMLやCSSを触ったことがある方なら理解できる内容になっています。

Web制作者のためのSassの教科書
Web制作者のためのSassの教科書 改訂2版

Sassの基本機能から応用まで学べる!

SCSS記法の様々な使い方

この章では、SCSSの様々な使い方について解説します。

ネストの色々な書き方

SCSS記法の特徴としてネスト構造がありますが、基本的な書き方に加えて他にも覚えておくと便利な書き方があるので解説していきます。

階層構造をネストする

下記のHTMLのコードでは、ulタグを親要素、liタグを小要素の階層になっています。
HTMLの要素やタグを復習したい方は、「HTMLタグと要素」を参考にして下さい。

HTML
1
2
3
4
5
<ul class="parent">
  <li class="child">テキスト1</li>
  <li>テキスト2</li>
  <li>テキスト3</li>
</ul>

この様な階層構造は、CSSで書くと下記の様に記述します。

CSSで記述した場合
1
2
3
4
5
6
7
.parent {
  color: red;
}

.parent .child {
  color: blue;
}

これをSCSS記法で書くと、小要素をネストして記述する事が出来ます。

SCSSで記述した場合
1
2
3
4
5
6
7
.parent {
  color: red;

  .child {
    color: blue;    
  }
}

プロパティをネストする

階層構造だけではなく、プレフィックスが同じプロパティもネストする事が出来ます。

HTML
1
<div class="content">中身</div>
CSS -->
1
2
3
4
5
.content {
  background-color: red;
  padding-top: 1em;
  padding-left: 2em;
}

SCSSで書くと下記の様な構造になります。

SCSS -->
1
2
3
4
5
6
7
.content {
  background-color: red;
  padding: {
    top: 1em;
    left: 2em;
  }
}

paddingの後のコロンの付け忘れに注意しましょう。

アンパサンド(&)を使ってネストする

SCSS記法では、アンパサンドと呼ばれる&を使ってネストすると、親セレクタを参照する事が出来ます。

例えば、通常CSSでリンクのホバー時に文字の色を変更する場合は、擬似要素を使って以下のコードのように記述します。

HTML
1
2
3
4
5
<ul>
  <li><a href="#">テキスト1</a></li>
  <li>テキスト2</li>
  <li>テキスト3</li>
</ul>
CSS -->
1
2
3
4
5
6
a {
  text-decoration: none;
}
a:hover { /* ホバー時に色を変更する */
  color: green;
}

これをアンパサンド(&)を使うと、以下のコードのように記述する事が出来ます。

SCSS -->
1
2
3
4
5
6
7
a {
  text-decoration: none;

  &:hover { /* &は親セレクタのaを参照する */
    color: green;
  }
}

ネストしている&は、親セレクタのaを参照してるので、上記の&:hovera:hoverとなります。

擬似要素をアンパサンドを使った場合

また、アンパサンド&は、擬似要素以外にも以下のコードのようにBEMが取り入れられたクラス名の場合に効率良く記述する事が出来ます。

HTML | BEMが取り入れられたクラス名
1
2
3
4
<form class="form">
  <input type="text" class="form__text">
  <input type="submit" class="form__button">
</form>
SCSS | BEMで命名されたクラス名に&を使用した場合
1
2
3
4
5
6
7
8
9
10
.form {
  &__text { /* &はformを参照するので form__textになる */
    font-size: 18px;
  }

  &__button { /* &はformを参照するので form__buttonになる */
    color: white;
    background-color: blue;
  }
}

CSSにコンパイル後は以下のコードのようになります。

CSS | CSSにコンパイル後のコード
1
2
3
4
5
6
7
.form__text {
  font-size: 18px;
}
.form__button {
  color: white;
  background-color: blue;
}

sassmeisterで確認すると、以下の動画のようになります。

sassmeisterでアンパサンドを確認する

このように、アンパサンド&を使うと親セレクタを参照する事が出来るので相性が良いと言われているBEMと合わせて利用すると更に効率良くコーディングする事が出来ます。

BEMについて詳細は、「BEMの使い方」を参考にしてください。

変数の定義

SCSSでは、変数を定義して利用する事が出来ます。

SCSSで変数を定義する
1
2
3
4
5
/* 変数の定義 */
$変数名: 

/* 変数を使う */
$変数名

変数の定義は、「$変数名: 値」で定義する事が出来きて、定義した変数は「$変数名」で利用出来ます。

メディアクエリのブレイクポイントのサイズやカラーなど共通する値を変数にすると使いまわす事が出来るので便利です。

SCSS -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 共通で使うカラーを定義 */
$color-main: #ff00ff;

a{
  text-decoration: none;

  &:hover {
    color: $color-main;
  }

  &::before {
    color: $color-main;
    content: "★";
  }
}

CSSにコンパイルされると下記の様なコードになります。

CSS -->
1
2
3
4
5
6
7
8
9
10
11
a {
  text-decoration: none;
}
a:hover {
  color: #ff00ff;
}
a::before {
  color: #ff00ff;
  content: "★";
}

変数で定義した「#ff00ff」の値がcolorに設定されているのが分かります。

演算

SCSSは、加算(+)、減算(-)、乗算(*)、除算(/)、剰余(%)を使って計算を行うと、コンパイル時に計算結果に変換してくれる便利な機能があります。

SCSS
1
2
3
4
.box {
    height:20px * 2;
    width:100px - 20px;
}

上記のSCSSのコードはCSSにコンパイルされると、計算結果がプロパティの値になっている事が分かります。

CSS -->
1
2
3
4
5
.box {
  /* プロパティ値が演算結果の値になる */
  height: 40px;
  width: 80px;
}

※除算だけは丸括弧()で囲むので注意してください。

SCSS
1
2
3
4
5
  /* 除算は丸括弧()で囲む */
.box {
    height:20px * 2;
    width:(1000px / 3);
}
CSS -->
1
2
3
4
.box {
  height: 40px;
  width: 333.3333333333px;
}

丸括弧()しなかった場合は「width:(1000px / 3);」がそのまま出力されます。

関数

SCSSでは、文字列や数値や色などを操作するための関数がたくさん用意されています。下記は、数ある中の関数の一部です。

関数 概要
unit($number) 値から単位を取得する
mix($color1, $color2, $weight) 2つの色を混ぜる
invert($color) 色を反転させる
alpha($color) 色の透明度を取得する
lighten($color, $amount) 指定の色を指定した割合だけ明るくする



指定した2つの色を混ぜる関数mix()を使ってみましょう。

HTML
1
<div class="box"></div>

mix()の引数には、混ぜる色2つを指定します。第3引数に割合を指定する事が出来ます。

SCSS
1
2
3
4
5
.box{
  background-color: mix(#000, #fff, 50%);
  height: 100px;   
  width: 100px;
}


第3引数の割合を変える事で色に変化を付けることが出来ます。

mix()の実行結果

この様にSCSSには、便利な関数が沢山提供されています。

SCSS
1
2
3
4
5
6
7
8
/* 変数で定義 */
$base-color1: #000;
$base-color2: #fff;

.box{
  background-color: mix($base-color1, $base-color2, 50%);
  /* 中略 */
}

ディレクティブ

「@名前」で指定することの出来る命令をディレクティブと言います。SCSSでは、関数やmixinと呼ばれる様な様々な処理制御にディレクティブが対応します。

これからSCSSの代表的な4つのディレクティブを解説していきます。

  1. @functionディレクティブ - 自作で関数を定義する事が出来る
  2. @mixinディレクティブ - スタイル定義を再利用する
  3. @extendディレクティブ - 定義済みのスタイルを継承する
  4. @importディレクティブ - 外部のスタイルシートを読み込む

1.@functionディレクティブ

SCSSは、先ほど解説したmix関数の様に提供される関数もありますが、自作で関数を作成することも出来ます。

@functionディレクティブを使って定義します。また、@returnディレクティブを使った戻り値を返します。

SCSS
1
2
3
4
5
6
7
/* 関数を定義する */
@function 関数名(引数) {
    @return 戻り値;
}

/* 関数の呼び出し */
関数名(引数);

簡単な例を見ていきましょう。下記は「引数で指定した値($value) ÷ 2」の結果を返すsample関数を定義しています。

SCSS
1
2
3
4
5
6
7
@function sample($value) {
    @return $value / 2;
}

div {
    width: sample(100px);
}

上記のコードがコンパイルされると、下記のようになります。

CSSへコンパイル
1
2
3
div {
  width: 50px;
}

何度も利用する処理を関数で定義することによって、汎用性があるシンプルなコードになります。

2. @mixinディレクティブ

@mixinディレクティブは、ミックスインとも呼ばれており、スタイル定義を再利用するための仕組みです。@mixinディレクティブでスタイルを定義して、@includeディレクティブで使う場所に埋め込みます。

SCSS
1
2
3
4
5
6
7
8
9
10
11
/* 定義 */
@mixin common-box {
    background-color: #222;
    color: #eee;
}

.menu {
    /* 定義したミックスインを埋め込み */
    @include common-box;
    padding: 10px;
}

上記のコードをCSSでコンパイルすると下記の様になります。includeしたスタイルが.menuとして挿入されていることが分かります。

CSS
1
2
3
4
5
6
.menu {
  background-color: #222;
  color: #eee;
  padding: 10px;
}

@mixinディレクティブは、あらかじめ定義しておいたミックスインを別の場所に再利用する目的で利用されます。

また、@mixinディレクティブは引数でプロパティ値を変えることができます。

SCSS
1
2
3
4
5
6
7
8
9
10
@mixin box($background-color, $color) {
    background-color: $background-color;
    color: $color;
}

.menu {
    /* 引数に値を指定する */
    @include box(#222, #eee);
    padding: 10px;
}
ポイント
  1. @mixinディレクティブでスタイルを定義し、@includeディレクティブで使う場所に埋め込む
  2. @mixinディレクティブは、引数を渡すことが出来る
  3. プロパティ値だけ変更してスタイルを使いたい場合は、@mixinディレクティブを使う

3. @extendディレクティブ

@extendディレクティブは、定義済みのスタイルを他のクラスに継承することが出来ます。

以下のコードのように、@extendの後にセレクタ名(.sample)を引用符で囲まずに指定すると、そのクラスに.sampleのスタイルを継承する事が出来ます。

@extendディレクティブの定義方法 -->
1
2
/* @extend セレクタ名; */
@extend .sample;

例えば定義済みの.base-contentのスタイルをcontent1に継承したい場合は、以下のコードのように記述します。

SCSS | .base-contentのスタイルを継承する場合-->
1
2
3
4
5
6
7
8
9
10
11
.base-content {
  border: 1px solid grey;
  font-size: 20px;
  padding: 20px;
  width: 200px;
}

.content1 {
  @extend .base-content; /* .base-contentを継承する */
  border-color: red; /* borderの色だけgreyからredに変更したい*/
}

CSSにコンパイルされると、以下のコードのように同じプロパティを持つスタイルはまとめられます。そして、content1に独自で定義したborder-color: redは、個別に出力されます。

CSS | コンパイル後-->
1
2
3
4
5
6
7
8
9
10
.base-content, .content1 { /*同じスタイルはまとめられる*/
  border: 1px solid grey;
  font-size: 20px;
  padding: 20px;
  width: 200px;
}

.content1 { /*独自で定義したスタイルは個別に記述される*/
  border-color: red;
}

このように、@extendディレクティブを使えば同じプロパティはまとめてくれるので、余計な重複コードを出力してしまうという心配がありませんね。

sassmeisterで上記の一連の挙動を確認すると、以下の動画のようになります。

extendディレクティブの使用例

ポイント
  1. @extendディレクティブは、他のスタイルを継承し独自に変更する事が出来る
  2. コンパイル後のCSSでは、共通するプロパティは全てまとめられて、独自に定義したスタイルは、個別に出力される
  3. コンパイル後のコード量を減らしたい場合に使う
プレースホルダーセレクタ

@extendディレクティブは、セレクタを継承することが出来るのでコンパイルされたCSSに継承元のセレクタも生成されます。

コンパイル後のCSSに出力される継承元のセレクタ

しかし、継承専用のセレクタとして扱いたいなど継承元のセレクタがコンパイル後に不要な場合は、プレースホルダーセレクタ(Placeholder selector)という専用のセレクタを使えば解決することが出来ます。

プレースホルダーセレクタは、以下のコードのようにid(#)やクラス(.)の代わりに%をつけて定義します。

プレースホルダーセレクタの定義-->
1
2
3
4
/* .sampleにプレースホルダーセレクタを使う場合 */
%sample {
  color: red;
}

例えば、先ほどの.content1.base-contentを継承したコードをプレースホルダーセレクタに変更すると、以下のコードのようになります。

SCSS | プレースホルダーセレクタに変更した場合-->
1
2
3
4
5
6
7
8
9
10
11
%base-content { /* .base-contentから%base-contentに変更 */
  border: 1px solid grey;
  font-size: 20px;
  padding: 20px;
  width: 200px;
}

.content1 {
  @extend %base-content; /* ここも%base-contentに変更 */
  border-color: red;
}

上記のコードがコンパイルされたCSSは、以下のように継承元のセレクタが生成されていない事が分かりますね。

プレースホルダーセレクタを使った場合のコンパイル

ポイント
  1. @extendディレクティブは、継承元のセレクタもコンパイル後のCSSで生成されてしまう
  2. コンパイル後に継承元のセレクタが不要な場合は、プレースホルダーセレクタ%でセレクタを指定する
@extendと@mixinの違いとは?

@extendディレクティブと似た機能には、@mixinディレクティブがありましたね。
2つの違いや使い分けは、以下の通りです。

  • @extendディレクティブは、関連性のあるルール同士に使う
  • @mixinディレクティブは、関連性のないルール同士に使える
  • プロパティ値だけ変えてスタイルを使う場合は、@mixinディレクティブを使う

まずは、@extendディレクティブを見ましょう。

以下のコードの.base-buttonは、全てのボタンのベースとなるスタイルを設定してます。.green-buttonの場合はボタンと関連性があるので、.base-buttonを@extendで継承する事が出来ます。

SCSS | @extendディレクティブの使いどころ-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 /* 全てのボタンのベースのスタイルを設定 */
.base-button {
  border-style: solid;
  display: inline-block;
}

/* buttonに関連性があるので、@extendで.base-buttonを継承出来る*/
.green-button {
  @extend %base-button;
  background: green;
}

/* .base-buttonのスタイルを使いたいが、関連性がないので@extendで継承しない!*/
.footer {
  background: grey;
}

しかし、.footerの場合はボタンと全く関連性がないので、.base-buttonのスタイルを使いたくても@extendで継承することはありません。このように@extendディレクティブは、関連性のあるルール同士に使います。

次は、@mixinディレクティブを見ましょう。

以下のコードのように@mixinで定義するbase-fontは、フォントのベースとなるスタイルを設定しています。@mixinディレクティブは、@extendディレクティブと違ってフォントに関連性のない.nav.headerなどに対してbase-fontを@includeして使う事が出来ます。

SCSS | @mixinディレクティブの使いどころ-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* フォントのベースのスタイルを設定 */
@mixin base-font {
  color: #575f74;
  font-family: sans-serif;
}

.nav {
  @include base-font;
}

.header {
  @include base-font;
}

.footer {
  @include base-font 
}

このように@mixinディレクティブは、関連性のないルール同士に使う事が出来ます。

また、@mixinディレクティブは引数を渡す事も出来るので、以下のコードのようにプロパティ値だけ毎回変えて使いたい場合にも便利です。

SCSS | @mixinディレクティブの使いどころ-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@mixin base-font($color) {
  color: $color;
  font-family: sans-serif;
}

.nav {
  @include base-font(#575f74);
}

.sidber {
  @include base-font(#4b505e); 
}

.footer {
  @include base-font(#474a52);
}
ポイント
  1. @extendディレクティブは、関連性のあるルール同士に使う
  2. @mixinディレクティブは、関連性のないルール同士に使える
  3. プロパティ値だけ変えてスタイルを使う場合は、@mixinディレクティブを使う

4. @importディレクティブ

@importディレクティブは、外部のスタイルシートである「.scssの拡張子のファイル」をインポートする機能です。指定したファイルは、CSSにコンパイルされると1つのファイルとして書き出されます。

SCSS
1
2
3
4
5
/*  ファイルをインポートする  */
@import "ファイル名.scss"; 

/* 拡張子は省略可能なので、下記でも良い*/
@import "ファイル名"; 

下記の様に、カラーをまとめたファイルをインポートした時の例を見てみます。

color.scss
1
2
3
/* 色をまとめたファイル */
$base-black: #383838;
$base-write: #fff;
SCSS
1
2
3
4
5
6
7
/* 拡張子は省略可能 */
@import "color.scss";

/* color.scssで定義したカラーを利用する */
main {
  color: $base-black;
}

上記のコードをCSSへコンパイルすると、下記の様になります。

CSSへコンパイル -->
1
2
3
main {
  color: #383838; 
}

変数でカラーコードを定義する事によって、使い回せるのはもちろんですが、1つ1つ何を意味しているのか分かりやすくなりコードの可読性も上がります。特にミックスインを外部ファイル化すると、必要な時に@importで呼び出す形にするとコードの保守性が上がり管理しやすくなります。

SCSS記法の応用的な使い方

この章では具体例を挙げて実際にどのような場面でSCSS記法が便利になるのかを説明していきます。

@mixinディレクティブ

@mixinディレクティブは、SCSS記法の様々な使い方のmixinディレクティブの章でスタイル定義を再利用するための仕組みと説明しました。

この章ではmixin関数を定義してスタイルを再利用できる様になることで、どのような場面で便利になるのか具体例を使って説明します。

例えば、信号がよく出現する道路交通ルールについて勉強できるサイトがあるとします。

以下の様に北交差点にある信号機のhtmlとscssがあります。

See the Pen mixin関数を定義する前 by miyajima yuya (@pikawaka) on CodePen.

このとき赤色だけopacity: 1;のスタイルが定義されているので、今は決め打ちで赤色が点いている状態です。
これを決め打ちではなく、mixin関数を定義して関数の引数によって光る信号の色を決められると便利ではないでしょうか?

引数によって光る信号の色を変えるには、以下の2点を定義すると実現できます。

  1. 関数を呼んだ時に必ず読み込まれるスタイルの定義
  2. SCSS記法の条件分岐

関数を呼んだ時に必ず読み込まれるスタイルの定義

まずは既存のスタイルをmixin関数に置き換えます。
変更前は以下のファイルの内容でしたね。

変更前のSCSSファイル -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.nourth-intersection {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
      opacity: 1; //赤色だけ追加
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
}

上記のコード↑を以下のコード↓に変更します。

変更後のscssファイル | mixinを使って引数によって光る色を決める -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
@mixin current-signal($color) {
/* nourth-intersectionクラスは信号クラスを定義するクラスではないので
mixinには含めません。*/
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
  //ここから追加
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}

// @mixinで定義したスタイルを@includeで呼び出す
.nourth-intersection {
  @include current-signal(red);
}

変更後のscssファイルを見てみると@mixin current-signal($color) {~~~}にはmixinの関数が定義されていて、.nourth-intersection {@include current-signal(red);}では、mixinで定義したcurrent-signal(red)をincludeしてスタイルを埋め込んでいます。

current-signal(red)のスタイルの中身を確認します。
下記のコードのスタイルは、current-signal関数が呼ばれた時に必ず読み込まれるスタイルになります。

current-signal関数のscssファイル | 関数を呼び出した時に必ず読み込まれるスタイル -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.signal {
  height: 100px;
  width: 300px;
  background-color: gray;
  display: flex;
  .signal-child {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    opacity: 0.2;
  }
  .red-signal {
    background-color: red;
  }
  .yellow-signal {
    background-color: yellow;
  }
  .blue-signal {
    background-color: blue;
  }
}

このスタイルが読み込まれることによって信号の見た目のベースができます。
親の.nourth-intersectionにネストして上記のスタイルを定義することで、見た目のベースができていることを確認できます。

See the Pen mixinの説明の既存で必ず読み込まれるスタイル by miyajima yuya (@pikawaka) on CodePen.

current-signal関数はこの信号のベースのスタイルができた前提で、条件分岐を使ってどの信号を光らせるか指定できる関数になります。

SCSS記法の条件分岐

元々のscssファイルを見てみると、@if ~ @elseが追加されていることが分かります。このscssファイルの中のifは、rubyのifやその他の言語のifと使い方はほとんど同じです。

current-signal関数のscssファイル | ifの部分だけ抜粋 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@mixin current-signal($color) {
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}

.nourth-intersection {
  @include current-signal(red);
}

current-signal(red)は引数にredを渡しているので、@mixin current-signal($color)で$colorはredを受け取ります。

つまり@if $color == redでは$colorはredなので、.red-signal {opacity: 1 !important;}のスタイルが読み込まれます。それ以降の@else ifなどは読み込まれません。

.nourth-intersectionのスタイルは以下の様なスタイルに置き換わったとイメージしてください。

current-signal関数のscssファイル | includeした結果 -->
1
2
3
4
5
6
7
8
9
10
11
12
// before
.nourth-intersection {
  @include current-signal(red);は以下のスタイルに置き換わる
}

// after
.nourth-intersection {
  // @include current-signal(red);は以下のスタイルに置き換わる
  .red-signal {
    opacity: 1 !important;
  }
}

そしてコンパイル後のcssは下記の様になります。

コンパイル後のCSS -->
1
2
3
.nourth-intersection .red-signal {
  opacity: 1 !important;
}

では、@include current-signal(yellow)にした場合はどうなるか確認します。

current-signal関数のscssファイル | yellowを引数に渡した場合 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@mixin current-signal($color) {
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}

.nourth-intersection {
  @include current-signal(yellow);
}

今回は@if $color == redには当てはまらないので、skipされます。次の@else if $color == yellowには条件が当てはまり、下記の様なcssにコンパイルされます。

コンパイル後のCSS -->
1
2
3
.nourth-intersection .yellow-signal {
  opacity: 1 !important;
}

次は、if、else ifの条件にない引数を指定した場合を見てみましょう。

current-signal関数のscssファイル | allを引数に渡した場合 -->
1
2
3
.nourth-intersection {
  @include current-signal(all);
}

どの条件も当てはまらないとなり@else {.signal-child {opacity: 1 !important;}が読み込まれ、下記の様にcssがコンパイルされます。

コンパイル後のCSS -->
1
2
3
.nourth-intersection .signal-child {
  opacity: 1 !important;
}

続いて、引数を渡さない場合はどうなるかみてみましょう。

current-signal関数のscssファイル | allを引数に渡した場合 -->
1
2
3
4
.nourth-intersection {
  // Error: Missing argument $color.とエラーが出る
  @include current-signal();
}

何も渡さなかった場合は、Error: Missing argument $color.とエラーがでてCSSにコンパイルされません。
引数を渡さない場合は、デフォルト引数を使います。

@mixin current-signal($color)となっている部分を@mixin current-signal($color: blue)に修正します。
この様にデフォルト引数を指定すると、@include current-signal();としても$colorにはデフォルトでblueが入った状態で処理されます。

current-signal関数のscssファイル | 引数を渡さなかった場合 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// デフォルト引数にblueを指定
@mixin current-signal($color: blue) {
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}

.nourth-intersection {
  @include current-signal();
}

上記↑のscssは下記↓のcssにコンパイルされます。

コンパイル後のCSS -->
1
2
3
.nourth-intersection .blue-signal {
  opacity: 1 !important;
}

基本的には青信号を付けることが多いので、引数を渡さない場合は青信号を点ける様にしたいという場合に便利ですね!

@mixin関数に引数と条件分岐を使った場合のまとめはこの様になります。

ポイント
  1. 引数に渡した$colorの値によってif ~ else ifの条件に当てはまったスタイルを読み込む
  2. 引数に渡した$colorの値がif ~ else ifのどの条件にも当てはまらない場合は、elseのスタイルを読み込む
  3. 引数に何も渡さない場合は、デフォルト引数が適用される

mixin関数を再利用して便利さを実感しよう

このcurrent-signal関数を再利用することでどう便利になるか見ていきましょう。

current-signal関数の中身が読める様になったので、まず一度全体を見直してみましょう。
全体はこの様になってました。

元々のscssファイル | mixinを使って引数によって光る色を決める -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@mixin current-signal($color) {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}

.nourth-intersection {
  @include current-signal(red);
}

.nourth-intersectionのクラスに@include current-signal(red);をつけているので、今は赤信号が点いている状態です。

See the Pen gOrVoeg by miyajima yuya (@pikawaka) on CodePen.

黄色と青色が半透明になっているのはopacity: 0.2を指定しているからになります。

元々のscssファイル | .signal-childを抜粋 -->
1
2
3
4
5
6
.signal-child {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  opacity: 0.2;
}

基本的には赤色と黄色と青色はopacity: 0.2を指定して半透明になっているのですが、@include current-signal(red);を使って.red-signal {opacity: 1 !important;}のスタイルを埋め込んでいるので、赤信号が付くことになります。

mixin関数に引数を渡して光る色を簡単に指定できます。

つまりmixin関数の良いところは

  • スタイル定義を再利用できるところ
  • 引数や条件分岐を使ってスタイルを柔軟に変えられるところ

ですから、使う回数が多ければ多いほどmixinは便利になります。
今は北交差点しかありませんが、東交差点、南交差点、西交差点を追加しましょう。

htmlファイル | 北交差点に東交差点、南交差点、西交差点を追加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div class="nourth-intersection">
    <p>北交差点</p>
    <div class="signal">
      <div class="signal-child red-signal"></div>
      <div class="signal-child yellow-signal"></div>
      <div class="signal-child blue-signal"></div>
    </div>
</div>

<!--ここから追加-->
<div class="east-intersection">
    <p>東交差点</p>
    <div class="signal">
      <div class="signal-child red-signal"></div>
      <div class="signal-child yellow-signal"></div>
      <div class="signal-child blue-signal"></div>
    </div>    
</div>

<div class="south-intersection">
    <p>南交差点</p>
    <div class="signal">
      <div class="signal-child red-signal"></div>
      <div class="signal-child yellow-signal"></div>
      <div class="signal-child blue-signal"></div>
    </div>    
</div>

<div class="west-intersection">
    <p>西交差点</p>
    <div class="signal">
      <div class="signal-child red-signal"></div>
      <div class="signal-child yellow-signal"></div>
      <div class="signal-child blue-signal"></div>
    </div>    
</div>

続いてscssファイルを修正します。

変更後のscssファイル | 北交差点に東交差点、南交差点、西交差点にスタイルを追加した場合 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@mixin current-signal($color: blue) {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
  @if $color == red {
    .red-signal {
      opacity: 1 !important;
    }
  } @else if $color == yellow {
    .yellow-signal {
      opacity: 1 !important;
    }
  } @else if $color == blue {
    .blue-signal {
      opacity: 1 !important;
    } 
  } @else {
    .signal-child {
      opacity: 1 !important;
    }
  }
}
.nourth-intersection {
  @include current-signal(red);
}
// ここから追加
.east-intersection {
  @include current-signal(yellow);
}
.south-intersection {
  @include current-signal();
}
.west-intersection {
  @include current-signal(all);
}

この様に変更すると、下記の様に表示されます。

See the Pen codepen_final by miyajima yuya (@pikawaka) on CodePen.

東交差点には yellow、南交差点には引数を渡さずデフォルト引数のblue、西交差点にはallを渡してそれぞれ指定したい部分で色が点いていることが分かります。

これをもしmixinを使わなかった場合は、大袈裟に書くと下記の様に一つ一つ交差点のクラスを指定してスタイルも指定しなければなりません。

scssファイル | mixinを使わなかった場合-->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
.north-intersection {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
      opacity: 1;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
}

.east-intersection {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
      opacity: 1;
    }
    .blue-signal {
      background-color: blue;
    }
  }
}

.south-intersection {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 0.2;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
      opacity: 1;
    }
  }
}

.west-intersection {
  .signal {
    height: 100px;
    width: 300px;
    background-color: gray;
    display: flex;
    .signal-child {
      width: 100px;
      height: 100px;
      border-radius: 50%;
      opacity: 1;
    }
    .red-signal {
      background-color: red;
    }
    .yellow-signal {
      background-color: yellow;
    }
    .blue-signal {
      background-color: blue;
    }
  }
}

この様にそれぞれの交差点に一つ一つスタイルを追加するのは大変で管理し辛いですね。しかしmixinを使うとこの様にすっきり書けるというわけです。

scssファイル | mixinを使った場合 -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.nourth-intersection {
  @include current-signal(red);
}

.east-intersection {
  @include current-signal(yellow);
}

.south-intersection {
  @include current-signal(blue);
}

.west-intersection {
  @include current-signal(all);
}

明らかにmixinの方が管理しやすく便利なのが分かりますね。よく使うスタイルがあればmixinを使う様にしましょう。

おすすめ

RailsのCSS設計についてもっと詳しく知りたい方は、こちらのPikawaka動画『RailsエンジニアのためのCSS設計の解説動画』もおすすめです!

Railsアプリケーションによく取り入れられる「FLOCSS・BEM・SCSS」を組み合わせたCSS設計方法を動画で解説しています。

この記事のまとめ

  • Sassとは、CSSのスタイルシート言語の機能を拡張した言語のこと
  • 「SASS記法」と「SCSS記法」の2種類ある
  • Sassを使うことで、保守性が高まり人為的なミスを減らすことが出来る