yasuda

Sassの使い方 – 誰にとっても使いやすいCSSにするための工夫

CSS Niteで発表されたコーダー白書2016(19ページを参照)によると、業務でHTMLコーディングをしている人のうち、約70%がSassを日常的に使っているそうです。
CSSプリプロセッサーにはStylusやLESS、もしくはPostCSSのようなツールを使う場合もありますが、上記のアンケートの結果や色々なCSSフレームワークを見ていると、Sassがデファクトスタンダートであることは間違いないようです(Sassは基本的に.scssの記法を想定しています)。

Sassは多機能、でもすべてを使わないといけないわけではない

Sassで書かれたコードを見ていると、読みにくいコードや複雑になりすぎているコードをよく見かけます。
SassはCSSにプログラミングの機能を持ち込んでいるので、できることは増えましたが、使い方次第で「書いた人が書いたときにだけ分かるコード」になってしまいがちです。

CSSはさわる人が多くなる分、経験や知識の差にバラツキがあることも多いと思います。
だからこそ、「使う機能」だけではなく「使わない機能」を決めておくことで、誰にとっても使いやすいCSSにしていくことができるんじゃないかなと思っています。

今回はSassを使っていて感じた、Sassの積極的に使いたい機能と避けたほうがいい機能を紹介します。

Sassの積極的に使いたい機能

Sassのなかで特に使っていきたい機能としては以下の4つがあります。

  • 最小限の変数
  • メディアクエリを管理するmixin
  • 制限されたネスト
  • ファイルを分けて管理する@import

最小限の変数

CSSの変数であるCSS Custom Propertiesブラウザの対応状況が十分ではないので、Sassの変数はまだまだ使っていく必要があります。

例えば、以下のようにサイト内で2回以上指定するような値を変数として定義しておくと、変更があったときにもコードの書き換えは最小限にできます。

$max-width: 1080px !default;
$color-brand: gold !default;
$font-famiry-sans-serif: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
$font-famiry-serif: "Hiragino Mincho ProN", "游明朝", "YuMincho", "HG明朝E", Meiryo, serif !default;

以下のようにfont-familyのセットを保存しておくのもおすすめです。

// 和欧混植、Helvetica優先、游ゴシックなし
$font-family-sans-serif-1: "Helvetica Neue", Helvetica, "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
// 和欧混植、Helvetica優先、游ゴシック優先
$font-family-sans-serif-2: "Helvetica Neue", Helvetica, "游ゴシック", YuGothic, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
// 和欧混植、Verdana優先、游ゴシックなし
$font-family-sans-serif-3: Verdana, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
// 和欧混植、Verdana優先、游ゴシック優先
$font-family-sans-serif-4: Verdana, "游ゴシック", YuGothic, "Hiragino Sans", "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
// 和文のみ、游ゴシックなし
$font-family-sans-serif-5: "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;
// 和文のみ、游ゴシック優先
$font-family-sans-serif-6: "游ゴシック", YuGothic, "Hiragino Kaku Gothic ProN", Meiryo, sans-serif !default;

メディアクエリを管理するmixin

以前記事に書きましたが、メディアクエリはmixinで管理することでメンテナンスがしやすくなります。

Sassの変数とmixinで変更に強いメディアクエリをつくる

例えば、以下のように指定すると、

.foo {
  color: blue;

  @include mq() {
    color: yellow;
  }

  @include mq(lg) {
    color: red;
  }
}

このように出力されます。

.foo {
  color: blue;
}
@media (min-width: 768px) {
  .foo {
    color: yellow;
  }
}
@media (min-width: 1000px) {
  .foo {
    color: red;
  }
}

メディアクエリの値は1箇所の変数(Map形式)で管理ができるので、値を柔軟に変更できます。

$breakpoint-up: (
  'sm': '(min-width: 400px)',
  'md': '(min-width: 768px)',
  'lg': '(min-width: 1000px)',
  'xl': '(min-width: 1200px)',
) !default;

制限されたネスト

ネスト(入れ子)にしたセレクタはSass側でうまく変換して出力してくれます。

// Sass Input
.foo {
  color: blue;

  &:hover {
    color: green;
  }

  &.is-active {
    color: red;
  }

  @include mq(md) {
    color: green;
  }
}
/* CSS Output */
.foo {
  color: blue;
}

.foo:hover {
  color: green;
}

.foo.is-active {
  color: red;
}

@media (min-width: 768px) {
  .foo {
    color: green;
  }
}

基本的には擬似要素と擬似クラス、Stateクラスやメディアクエリでの利用にとどめます。
ネストの避けたい使い方は後述する「新しいキーセレクタを生成するネスト」で紹介します。

ファイルを分けて管理する@import

CSSは設計思想にもとづいて書くことが多くなってきました。例えば、FLOCSSSMACSSECSSなどです。

CSSの設計 – FLOCSSをベースにしたファイルの構成と命名規則を考える

これらの設計手法に共通するのは、ファイルを機能ごとに分類して管理すること、そのファイルをルールに従って1つのファイルに結合することです。
Sass以前のCSSでは、reset.cssとcommon.css、ページ専用のindex.cssなどを作成して別々に読み込むということをしていましたが、モダンな設計手法ではCSSは最終的に1つのファイルになることを前提に管理をしていきます。

例えば、FLOCSSでは以下のような例が示されています。

// ==========================================================================
// Foundation
// ==========================================================================

@import "foundation/_reset";
@import "foundation/_base";

// ==========================================================================
// Layout
// ==========================================================================

@import "layout/_footer";
@import "layout/_header";
@import "layout/_main";
@import "layout/_sidebar";

あるいは、ECSSでは以下のようなGlobパターンを使うことが示されています(以下はgulp-sass-globを使った例)。

@import "base/variable/**/*.scss";
@import "base/function/**/*.scss";
@import "base/mixin/**/*.scss";
@import "base/_normalize.scss";
@import "base/_base.scss";

@import "Structure/**/*.scss";
@import "SiteWide/**/*.scss";
@import "module/**/*.scss";

いずれの場合も、ファイルを分割することで、どんなコンポーネントがあるのか把握しやすくなり、ひとつひとつのファイルのコードの量が少なくなるので理解しやすくなります。カスケーディングの管理がしやすくなるという大きなメリットもあります。

個人的には、Sassを使ういちばんのメリットは@importだと感じています。

Sassの避けたほうがいい機能

Sassの避けておきたい機能としては以下の6つがあります。

  • Compass
  • 新しいキーセレクタを生成するネスト
  • 複数の引数をもたせた複雑なmixin
  • 増やしすぎた変数
  • @ifでの出しわけ
  • 別のファイルから呼び出すextend

Compass

CompassはSassの機能を拡張させるツールで、CSSスプライトやクロスブラウザ対応、便利なmixinを多数備えています(CompassはSassの機能というわけではありませんが、検索するとよく見かけるのでとりあげました)。

ただし、多機能がゆえに重かったり、クロスブラウザ対応もAutoprefixerのほうがメリットが大きかったりします。便利なmixinもCompass以外にもたくさん出ています。
GitHubでcompassgulp-compassのアップデート状況を確認すると、2年以上更新が止まってしまっていることを踏まえても、あえて今Compassを選択する理由というのも薄いのかなと思います。

新しいキーセレクタを生成するネスト

先ほどネストに関して、「基本的には擬似要素と擬似クラス、Stateクラスやメディアクエリでの利用にとどめます」としました。
なぜ制限をかけているのかというと、新しいキーセレクタを生成する書き方を避けたいからです。

キーセレクタというのは実際にスタイルが適応されるセレクタのことで、以下のコードでは.barがキーセレクタです。

.foo .bar {}

新しいキーセレクタを生成するネストはBEM(MindBEMding)で書くときに起こりがちです。

.Block {
  color: blue;

  &--Modifier {
    color: yellow;
  }

  &__Element1 {
    color: green;
  }

  &__Element2 {
    color: gray;
  }

  &__Element3 {

    &--Modifier {
      color: red;
    }
  }

}

上記のコードはこのように出力されます。

.Block {
  color: blue;
}

.Block--Modifier {
  color: yellow;
}

.Block__Element1 {
  color: green;
}

.Block__Element2 {
  color: gray;
}

.Block__Element3--Modifier {
  color: red;
}

コンパイル後のCSSよりもSass(特に最後の&__Element3Modifierの指定)のほうが理解しにくいと思います。実際にはもっとElementが多くなったり、メディアクエリがいくつも追加されるので理解するのが更に難しくなってしまいます。
Sassを使うこととネストをすることはイコールではありません。自分も含めた誰にとっても読みやすいコードにしておいたほうがいいですよね。

Blockの名前を変更するときにElementなどの名前も自動で変更されるのがメリットだという意見もありますが、Block名が変わるよりElementを編集するほうが多いですし、テキストエディタの機能でも十分に補える範囲ではないかなと思います。

複数の引数をもたせた複雑なmixin

mixinは便利な機能ですが、複雑にしすぎると変更しにくくなってしまい、属人化してしまう恐れがあります。
基本的には引数なしのシンプルなmixinを使用して、引数は最大でも2つまでにしておくのがいいと思います。

@eachや@forといった処理も複雑にしてしまう要因になってしまうので、基本は手書きにして、何回も書き直しをしなければいけないといった状況になったら自動化をするという手順にしたほうがいいです。

ドキュメント化されていて把握がしやすく、継続的にメンテナンスされるのであれば問題はありません。そういった意味では、外部ライブラリもドキュメント化と継続的なメンテナンスがあるかどうかは重要です。

増やしすぎた変数

変数は便利ですが、増やしすぎても管理しきれなくなってしまいます。

CSSフレームワークやライブラリでは、コンポーネントの中身をさわらずに、変数を変更することでスタイルを変更するという目的で変数がよく使われています。
でも、通常のWebサイトでは必要以上に変数化する必要はないと思います。
BootstrapやFoundationがたくさんの変数を持っているのは、汎用性のあるCSSフレームワークだからです。

@ifでの出しわけ

CSSフレームワークやライブラリでは@ifを使って条件分岐をしていることがあります。
これもCSSフレームワークやライブラリだから使うのであって、通常のWebサイトでは必要以上に複雑にする必要はないと思います。

別のファイルから呼び出すextend

extendは継承を可能にする原理的には優れた機能です。

例えば、mixinの場合は以下のコードが、

@mixin foo() {
  display: block;
}

.another {
  display: none;
}

.bar {
  @include foo();
}

.baz {
  @include foo();
  font-size: 1rem;
}

このように記述順通りに出力されます。mixinはコピー&ペーストしているイメージです。

.another {
  display: none;
}

.bar {
  display: block;
}

.baz {
  display: block;
  font-size: 1rem;
}

extendだと以下のコードが、

%foo {
  display: block;
}

.another {
  display: none;
}

.bar {
  @extend %foo;
}

.baz {
  @extend %foo;
  font-size: 1rem;
}

以下のように定義した場所(%foo)に呼び出した場所(.bar.baz)のセレクタも追加されるので無駄のないコードが出力されます。ただし、Sassの記述順とCSSの出力順が変わってしまうことに注意する必要があります。

.bar, .baz {
  display: block;
}

.another {
  display: none;
}

.baz {
  font-size: 1rem;
}

同じファイル内でextendを使うのであれば問題は起きにくいですが、違うファイル間でextendを使った場合は予期せぬスタイルの衝突が起きてしまう可能性があります。つまり、無駄のないコードではあるけれど管理しにくいコードになってしまうかもしれないということです。

基本的にはextendを使わず、使ったとしても同じファイル内で使うようにしておいたほうがいいと思います。

参考記事:@extendを使うべき時、@mixinを使うべき時 | POSTD

さいごに

SassによってCSSは多機能になりましたが、だからといって複雑な書き方をする必要はないと考えています。
CSSをさわる人のなかには、経験が浅かったり、CSSにあまり詳しくない人がいることを想定して書いておかないと、長期的にはうまくいかないんじゃないかと感じています。

僕個人としては、今回紹介した4つの機能があればSassを使うメリットは十分にあると思っています。

  1. 最小限の変数
  2. メディアクエリを管理するmixin
  3. 制限されたネスト
  4. ファイルを分けて管理する@import

きれいに整理されたCSSよりも、誰にとっても読みやすくて、変更しやすいCSSを書いていきたいですね。


参考記事

新しいウェブ体験を作ろう TAMのPWA開発
お問い合わせはこちら