変数

変数を作る

Sass の変数は頭に$記号を付けて宣言します。以下は$colorという変数に#cf649aという値を定義する例です。

$color: #cf649a;

使う時は、単にその変数名を変わりに使うだけです。

a {
  color: $color;
}

連想配列

恐らく色情報というのは複数定義する必要がでてくるんじゃないかなと思います。例えば、ベースカラーやアクセントカラー、サブカラーみたいな具合に。
これは、-colorというような Suffix を色関連の変数には付けるようにしてもいいかもしれませんが、連想配列を使う方法もあります。これは()で囲んで定義します。

$colors: (
  base: #393939,
  accent: #cf649a,
  sub: #679999,
);

これからはmap-getという関数を使って値を取り出すことができます。

a {
  color: map-get($colors, 'accent');
}

ファイル分割

Sass では@import <file-path>;といいう構文で別の sass ファイルを取り込むことができます。

例えば、以下のような_base.scssindex.scssがあるとします。

// _base.scss

$font-size: 14px;
// index.scss
@import 'base';

body {
  color: $font-size;
}

最初に@import '_base';とすることでindex.scssでも_base.scssの内容が使えるようになります。baseで読み込んでいるのは入力ミスではなくそういう仕様で、別ファイルに読み込まれることを想定しているようなファイルは_をファイル名に付けるとディレクトリ構造が整えれます。

共通ブロックの使い回し

Mixin

ブロック(スニペット)が定義できて、@mixinを使います。

@mixin reset {
  list-style: none;
  padding: 0;
  margin: 0;
}

ul {
  @include reset();
}

Mixin は@include mixin-name()のような形で、そのセレクタに取り込めます。

/* 変換後 */
ul {
  list-style: none;
  padding: 0;
  margin: 0;
}

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

単純なブロックの時には、Mixin よりプレースホルダーセレクターがいいかもです。これは定義済セレクタを取り込めます。

%reset {
  list-style: none;
  padding: 0;
  margin: 0;
}

ul {
  @extend %reset;
}

@extendはセレクタをまとめる

@mixin foo {
  color: orange;
}
ul {
  @include foo();
}
ol {
  @include foo();
}

%foo {
  color: orange;
}
ul {
  @extend %foo;
}
ol {
  @extend %foo;
}

セレクタが、ul, olになってますね。

// @include
ul {
  color: orange;
}
ol {
  color: orange;
}
// @extend
ol,
ul {
  color: orange;
}

注意点

例えば1行のプレースホルダーセレクター 5 つを 100 個のセレクターで使うとどうなるか。これは 100 個のセレクタが 5 回再定義され、かな〜り長いセレクタになります。
3 行以上のものに制限したり、1,2 行なら汎用セレクターを HTML で使いましょう。

Mixin と引数

こっちにしかない機能です。その場で渡した値から、異なるかもしれないブロックを受け取ることができます。
例でget-gutterを定義しました。

$gutter: (
  sp: 0 0.3em,
  pc: 0 1em
);

@mixin get-gutter($type) {
  @if $type != 'sp' and $type != 'pc' {
    @error '`sp` か `pc` を指定してください';
  }

  padding: map-get($gutter, $type);
}

div {
  @include get-gutter('sp');
}

@media screen and (min-width: 768px) {
  div {
    @include get-gutter('pc');
  }
}

これには$typeという値を渡すことできます。(sppcに制限)正常なら$gutterから値を取得し、paddingで返します。

/* 変換後 */
div {
  padding: 0 0.3em;
}

@media screen and (min-width: 768px) {
  div {
    padding: 0 1em;
  }
}

関数

1 つ前の記事でpadding: ...だけを返す Mixin を作りましたが、もしかしたらそういう関数を使用したほうが適任かもしれません。関数を使うと値を加工することができます。

関数は@functionを使って定義できます。また@returnを使って何かしら値を返すようにする必要があります。(JavaScript などに似てます)

@function gutter($type) {
  // 他で使わないなら入れちゃっても
  $gutters: (
    sp: 0 0.3em,
    pc: 0 1em
  );

  // not + $gutters に @type (spかpc) が定義されているか
  // -> $type が sp と pc 以外の値の時
  @if not map-has-key($gutters, $type) {
    @error "#{map-keys($gutters)}を指定してください";
  }

  @return map-get($gutters, $type);
}

div {
  padding: gutter('sp');
}

@media screen and (min-width: 768px) {
  div {
    padding: gutter('pc');
  }
}

制御構文

@if

状態には真偽値(boolean)を返すようにします。また、@else if@elseは両略できます。

@if 状態 {
  // ...
} @else if 状態 {
  // ...
} @else {
  // ...
}

@each

連想配列などを回したりできます。汎用セレクタを量産する時とか。

$colors: (
  success: #018952,
  danger: #ed5e6b
);

@each $key, $color in $colors {
  .button-#{$key} {
    background: $color;
  }
}
/* 結果 */
.button-success {
  background: #018952;
}

.button-danger {
  background: #ed5e6b;
}

@for

同じように色々返す時。

@for $i from 1 through 3 {
  .mt-${i} {
    margin-top: $ipx
  }
}
/* 結果 */
.mt-1 {
  margin-top: 1px;
}

.mt-2 {
  margin-top: 2px;
}

.mt-3 {
  margin-top: 3px;
}

値を決めたいようなものは@function化すると Mixin の巨大化も防げるかもしれません。

役に立つカラー関数

Sass ではいくつか便利な関数が最初から定義されているので、それらを簡単に使うことができます。
むしろ関数の為に Sass を使っているようなものなのでぜひ使いましょう。

mix($color1, $color2, [$weight])

2つの色を混ぜます

color: mix(#fff, #000); // #888

lighten($color, $amount)darken($color, $amount)

それぞれ白か黒と混ぜる関数です。ホバー時に色を白く(黒く)したい時に手っ取り早く使えます。

color: lighten(#000, 30%); // #4d4d4d;
color: darken(#fff, 20%); // #ccc;

adjust_hue($color, $degrees)

色相を回転させます。

color: adjust_hue(#f00, 90%); // #80ff00

complement($color)

補色を取得。

color: complement(blue); // yellow

saturate($color, $amount)desaturate($color, $amount)

鮮やかにするか、鈍くするか。

color: saturate(blue, 30%); // #0d0df3;
color: desaturate(blue, 100%); // #888;

// `grayscale` と同じ
color: grayscale(blue); // #888;

この辺に色々定義してあります。

sass:color モジュール

新しくなったモジュールシステムではsass:color@useしてくることにより上記を扱えます。その場合上記のすべての関数はcolor.のようなプレフィックスを付ける必要があります。

@use "sass:color";

.selector {
  color: color.saturate(blue, 30%);
}

@use

@importに置き換わる新しいコード細分化の方法です。取り込み方法はどちらも同じです。

// `_foo.scss`を読み込む
@use "foo";

違いはここからです。@importの時はその中身をその行にコピペしたかのような解釈でしたが、@useの場合はfoo内の要素にはfoo.プレフィックスを付けて使います。

例えばfooの中には変数$colorが定義されてるとします。その時使う側では、

@use "foo";

.selector {
  color: foo.$color;
}

のような形になります。これは@mixin@functionなどのルールに対しても同様です。

@use "foo";

.selector {
   @include foo.awesome-mixin;
   width: foo.awesome-width-function();
}

ただし、セレクタ系の要素は@importの時と同じ扱いです。例えば以下のような Sass があるとして、

// _foo.scss
%container {
  max-width: 960px;
  margin: 0 auto;
}

.selector {
  color: orange;
}

// main.scss
@use "foo";

body {
  @extend %container;
  margin: 0;
}

これを変換すると大体以下のようになります。

.selector {
  color: orange;
}

body {
  max-width: 960px;
  margin: 0 auto;
}

body {
  margin: 0;
}

@importと同じですね。

別名

さて、先程からfooという名前で読み込んだ時、foo.というプレフィックスを付けて使っていますが、設計を失敗するなどして同じ名前のファイルが複数できてしまったりした時にこれだと困ることになります。

そんな時の為に@useは別名での取り込みをサポートしています。それには後ろにasを付けて別名を続けます。以下はfooxxx-fooという別名を付けてみてます。

@use "foo" as xxx-foo;

.selector {
  color: f.$color;
}

カスタマイズして取り込み

内部で定義した!defaultを付けた変数は取り込み時に上書きできるという機能があります。_foo.scssが以下のような場合があるとします。

$color: orange !default;

これが外部のライブラリで「俺たちは青を使いたいんだ!」という場合は、以下のようにwithを続けて置いて取り込むことで内部で$colorを使ってる箇所すべてを変更できます。

@use "foo" with ($color: blue);

.selector {
  color: f.$color;
}

ちなみにもし!default定義されて無い変数をwithで渡したりするとSassError: This variable was not declared with !default in the @used module.のようなエラーになります。

あわせ技

aswithどちらも使いたい時は、必ずasを先にする必要があります。

@use "foo" as xxx-foo with (
  $color: blue
);

@forward

@forwardルールは対象ファイルに書かれてる内容を現在のファイルAにすべて取り込みます。つまり現在のファイルAを@useして使う場合@forwardしてきたファイルの内容も使えることになります。
JavaScript エンジニアな人なら`export * from '...';のようなものだと考えると分かりやすいかもしれません。

使い方は単に@mixinなどをまとめて定義したファイルのパスに対して@forwardするだけです。

@forward "mixins/button";
@forward "mixins/link";

別名

mixins/_button.scssの中身がこんな感じだとします。

@mixin text {
  color: orange;
}

もし、textという名前の Mixin がmixins/linkでも定義されていて被るのを避けたい時、asと続けて別名を定義することで回避できます。

@forward "mixins/button" as button-*;
@forward "mixins/link" as link-*;

これでmixins/buttontextbutton-textmixins/linkではlink-textという名前で取り込まれます。

カスタマイズして取り込み

@useのように内部で定義した!defaultを付けた変数を取り込み時に上書きできます。

@forward "mixins/button" with ($color: orange);

また違いとしてこちらはwithの中でも!defaultが使えます。これにより上位モジュールから更に変更可能になります。

@forward "mixins/button" with ($color: orange !default);

一部だけ取り込み

showhideを続けると取り込む要素を絞り込めます。show指定した要素だけhide指定した要素以外という意味の違いがあります。

@forward "mixins/button" show $new-color, new-text;
@forward "mixins/button" hide $old-color, old-text;