現場で役立つ実践Sass(3)変数を使いこなす

連載

現場で役立つ実践Sass

変数はSassで最も使う機能の一つです。
変数を使うことでコードの再利用性が高くなるため、モジュール化やMixinには欠かせない機能です。

第三回目の今回は、Sassの変数まわりの仕様とTipsを紹介します。

※今記事はRuby Sass 3.4.21で検証して書かれています。LibSassや、Sassのバージョンが違う場合は、コンパイル結果が異なる場合があります。

Sass変数とCSSネイティブ変数について

Sassの変数は、コンパイルされたCSSでは、その変数の値に書き換えられます。なので当然そのままCSSとして使用することができます。
そして、現在策定中なのがCSSでも変数が使えるようになる CSS Variables (CSS Custom Properties) という仕様です。
ブラウザ対応状況は Can I Use を参考ください。(掲載時はFirefox, Chromeが対応)

CSSネイティブの変数は、Sassの変数と仕様が若干違うので、Sassで使っていた変数を全てCSSネイティブの変数で置き換えることにはならなそうです。見比べてみましょう。

Sass変数について

変数名の先頭に $ をつけて定義します。そのまま属性の値として記述できますし、インターポレーション( #{} )を使えば、セレクタや文中にも配置することができます。

// コンパイル前(SCSS
$base-font: "Roboto";
@import url(https://fonts.googleapis.com/css?family=#{$base-font}:400,700);

body {
    font-family: $base-font, sans-serif;
}

// コンパイル後(CSS)
@import url(https://fonts.googleapis.com/css?family=Roboto:400,700);
body {
    font-family: "Roboto", sans-serif;
}

コンパイル後のCSSでは、Sassの変数は定義した値と置き換わります。

CSSネイティブ変数について

変数名の先頭に -- をつけて定義し、 var(変数名) で呼び出します。

:root {
    --main-color: #06c;
    --accent-color: #006;
}
#foo h1 {
    color: var(--main-color);
}

CSSネイティブ変数は、ブラウザが変数として認識するので、変数名がそのままCSSの値の位置に入ります。

Sassはネイティブ変数をそのままコンパイルできます。コンパイル前とコンパイル後の違いはありません。

// コンパイル前(SCSS)
:root {
    --main-color: #06c;
    --accent-color: #006;
}
#foo h1 {
    color: var(--main-color);
}

// コンパイル後(CSS)
:root {
    --main-color: #06c;
    --accent-color: #006;
}
#foo h1 {
    color: var(--main-color);
}

Javascript や calc と組み合わせて動的に使えるのがネイティブ変数の強みですね。
サイトカラーや、サイズの指定などにはネイティブの変数。モジュールにはSassの変数。と、用途に応じて使いわけることができそうです。

CSSネイティブ変数のブラウザサポートが広がって、実務でも使える日がくるのが楽しみです。

変数のスコープ

Sassの変数にもスコープがあります。スコープとは変数を参照できる範囲のことです。
Sassの変数はネスト(ブロック)ごとにスコープされます。
ネスト内に書かれているスコープは、「ローカル変数」となり、そのネスト以下で有効です。ネスト外から参照することはできません。
ブロック内で宣言されていない変数は「グローバル変数」となり、どの箇所からも参照できます。

$default-color: red; // グローバル変数を定義

.main-page {
    color: $default-color; // グローバル変数なので参照できる
    $main-color: tomato; // ローカル変数を定義
}

.campaign-page {
    color: $main-color; // ローカル変数なので参照できない
}

数年前までのバージョンでは、ローカルスコープで同じ名前の変数を宣言すると、以降はグローバルの変数の値も変わってしまうため、ユニークな名前の変数を沢山作りましたが、現在はローカルスコープのみ変わるようになりました。

$default-color: red;

.main-page {
    color: $default-color; // red
}

.campaign-page {
    $default-color: tomato; //ローカルで同じ名前の変数宣言
    color: $default-color; // tomato
}

.sub-page {
    color: $default-color; // グローバル変数を参照するので red
}

同じモジュールでも特定のページだけ装いを変えたい時などに、ローカルスコープで変数を上書きする方法が有効です。
その為にもモジュールの設計の段階で、変数を取り入れましょう。

変数オプション

変数にフラグと呼ばれるオプションを付けると、変数の振るまいをコントロールすることができます。フラグは変数の値に続けて記述します。ここでは !global フラグと !default フラグの使い方を紹介します。

!global フラグ

!global フラグは、変数の宣言場所に関わらず、グローバル変数として値を上書きしたいときに使います。

$default-color: red;

.main-page {
    color: $default-color; // red
}

.campaign-page {
    $default-color: tomato !global; //!globalフラグでグローバル変数を上書き
    color: $default-color; // tomato
}

.sub-page {
    color: $default-color; // 上書きされたグローバル変数を参照するので tomato
}

!global フラグを宣言した行より後の変数に対して有効になります。

!default フラグ

!default フラグは、値の定義がない変数に使われる値を指定するためのフラグです。名前のとおりデフォルトの値です。

$default-color: red;

.main-page {
    color: $default-color; // red
}

.campaign-page {
    $default-color: tomato !default; // !defaultフラグを指定
    $campaign-color: green !default; // !defaultフラグを指定

    color: $default-color; // !defaultの前にグローバル変数が定義されているのでred
    background-color: $campaign-color; // !default以外で変数が定義されていないのでgreen
}

.sub-page {
    color: $default-color; // red
}

通常は、同名の変数がある場合、後に書かれた定義が優先されますが、!default フラグは優先されません。
モジュールなどで予め値を指定しつつ変更可能にしておく場合のように、上書きされる前提で !default フラグは使います。

変数には型がある

Sassの変数には、型(データタイプ)が存在しています。
現在、7種類に分類されサポートされます。

型については、つねに意識する必要はありませんが、Mixin や制御構文の際に理解しておくと、利便性が上がり、型の違いによって起こる予期せぬエラーを防ぐことができます。

型を確認する

型は type-of関数 で確認することができます。

// コンパイル前(SCSS)
.type-of {
    $number: 100px;
    $color: #f00;
    $string: "文字列";
    $boolean: false;
    $null: null;
    $list: 10, 20, 30, 40;
    $map: (a:0, b:1em, c:2em, d:3em);

    //type-of関数で型を取得
    content: "#{type-of($number)}";
    content: "#{type-of($color)}";
    content: "#{type-of($color)}";
    content: "#{type-of($string)}";
    content: "#{type-of($boolean)}";
    content: "#{type-of($null)}";
    content: "#{type-of($list)}";
    content: "#{type-of($map)}";
}

// コンパイル後(CSS)
.type-of {
  content: "number";
  content: "color";
  content: "string";
  content: "bool";
  content: "null";
  content: "list";
  content: "map";
}

type-of関数と@if

type-of関数@if を組み合わせて、特定の型の場合のみ実行することができます。

$value1: 100px;
$value2: #f00;

.block {
    // 型が数値だったときのみ実行される
    @if type-of($value1) == number{
        margin-top: $value1;
    }
    // 型が数値ではないので実行されない
    @if type-of($value2) == number{
        margin-top: $value2;
    }
}

変数に誤った値が定義された際、実行させないといった使い方ができます。

Color型と色を扱う関数

Color型と色を操作する関数を組み合わせると一つの色から様々なカラーパターンを作成することができます。

$color-base: #666;

$color-darkest:    darken($color-base, 45%); // black;
$color-darker:     darken($color-base, 30%); // #1a1a1a;
$color-dark:       darken($color-base, 15%); // #404040;
$color-light:      lighten($color-base, 15%); // #8c8c8c;
$color-lighter:    lighten($color-base, 30%); // #b3b3b3;
$color-lightest:   lighten($color-base, 45%); // #d9d9d9;

上の例では lighten関数darken関数 を使いましたが、他にも彩度を調整するsaturate/desaturate関数 や、色相環を回す adjust-hue関数、補色の complement関数 などがあります。

詳しくは Sassドキュメント で確認ください。

List型と@each

List型(配列)は @each で便利に使えます。
例えば、下記のようにSNSアイコン画像を一括で指定して、コンパイル時に展開することができます。

// コンパイル前(SCSS)
$icons: twitter, facebook, googleplus, instagram;

@each $icon in $icons {
    .icon-#{$icon} {
        background-image: url("img/#{$icon}.png");
    }
}

// コンパイル後(CSS)
.icon-twitter {
     background-image: url("img/twitter.png");
}
.icon-facebook {
     background-image: url("img/facebook.png");
}
.icon-googleplus {
     background-image: url("img/googleplus.png");
}
.icon-instagram {
     background-image: url("img/instagram.png");
}

アイコンの大きさが同じCSSスプライトであれば、index関数と組み合わせて下記のように位置指定に使うこともできます。

// コンパイル前(SCSS)
$icons: twitter, facebook, googleplus, instagram;
@each $icon in $icons {
    // インデックスキーを取得
    $index: index($icons, $icon);
    // 高さを指定
    $heigth: 50px;
    // 2つ目から50pxずつ引いてゆく
    $position: ($index * $heigth - $heigth) * -1;

    .icon-#{$icon} {
        background-position: left $position;
    }
}

// コンパイル後(CSS)
.icon-twitter {
    background-position: left 0px;
}
.icon-facebook {
    background-position: left -50px;
}
.icon-googleplus {
    background-position: left -100px;
}
.icon-instagram {
    background-position: left -150px;
}

配列をひとつずつ取得したい場合は nth関数 を使います。

.twitter {
    // リストの1番目を取得
    content: "#{nth($icons, 1)}"; // twitter
}

Sassのインデックスキーは1から始まります。

Map型と@each

Map型(連想配列)とはList型にキーと値の組み合わせを定義したものです。
定義する場合は全体を丸括弧で囲い、キーと値のペアをカンマ区切りで指定します。

$icons:(
    twitter: #55acee,
    facebook: #3b5998,
    googleplus: #dc4e41,
    instagram: #3f729b
);

改行はなくても問題ありません。

Map型で @each をより便利に使えます。
例えば前述のSNSアイコンが背景色だった場合、以下のようにして複数のクラス指定を生成できます。

// コンパイル前(SCSS)
$icons:(
    twitter: #55acee,
    facebook: #3b5998,
    googleplus: #dc4e41,
    instagram: #3f729b
);

//キーとバリューをカンマで指定
@each $key, $value in $icons {
    .icon-#{$key} {
        background-color: $value;
    }
}

// コンパイル後(CSS)
.icon-twitter {
    background-color: #55acee;
}
.icon-facebook {
    background-color: #3b5998;
}
.icon-googleplus {
    background-color: #dc4e41;
}
.icon-instagram {
    background-color: #3f729b;
}

連想配列から特定のキーに対応する値を取得したい場合は、map-get関数 を使います。

.facebook {
    background-color: map-get($icons, facebook); // #3b5998
}

今回は簡単な例を紹介しただけですが、変数として定義しておくことで、仕様変更や、モジュール化なども柔軟に対応できます。

変数を定義している場所がバラバラだと、後々編集しづらいサイトになってしまうので、_var.scss のような変数用のパーシャルファイルを作成することをオススメします。
また、第一回で紹介したBracketsの SASShints など、変数を補完するエクステンションをエディタに入れるとより便利になるでしょう。