Sass、そしてSassy CSS (SCSS)

CSSを拡張したメタ言語であるSass、そしてその別文法として定義されたSCSSについて、960.gsなどのCSSフレームワークと絡めて、Sass (主にSCSS)の良さを解説する。

CSSフレームワーク

960.gsBlueprintBlueTripなどCSSフレームワークと呼ばれるものは色々ある。フレームワークと名乗るだけのことはあって、それらの生産性はとても高い。テンプレートで適切にクラス名やIDを埋め込むだけなので、複雑怪奇なCSSコーディングを意識することなく誰でも簡潔にきれいなカラム・レイアウトを作成できる。

HTML 4.01の控えめな表現力とCSS 2.1の控えめな文法を考えると、こういったクラス名ベースのCSSフレームワークというのは妥協点として申し分のないものだと思う。つまりHTML5やCSS3どころかCSS 2.1の文法の半分くらいしか解釈できないブラウザがそこそこいる現状では、安全確実なクラス名ベースでのスタイルのカスタマイズにならざるを得ないということだ。都合の良いことにブログのブレイク以降テンプレートによるウェブサイトの一括生成というシステムが山のようにあり、それらではHTMLの修正からウェブサイト全体の再構築というのは非常に簡単になっている。そういう面でもHTMLを更新するだけで済むというのは非常に手軽なアプローチだろう。また細かく機能別に分けたクラス名を組み合わせて指定してデザインを整えるという仕組みは直感的で、CSSに疎い人にはうってつけだとも思う。

そんな便利なCSSフレームワークが白い目で見られることが多いのも事実だ。それはせっかく長い時間をかけてテーブル・レイアウトのようなゴミを駆逐して構造とデザインの分離が一歩進み、HTML5とCSS3でなんとか満足行く程度には達成されそうになったのにも拘らず、またHTMLをゴリゴリいじるのが流行るのかというような嘆息からそうなるのだと思う。でもこれは今ポピュラーなCSSフレームワークやグリッドシステムの実装が悪いわけでもなく(前段落で書いたように現状に即した妥協に過ぎない)、HTMLやCSSの目指す構造とデザインの分離が理想に過ぎるというわけでもない。悪いのはCSSが言語として貧弱すぎ、フレームワーク(的なもの)をCSSだけで完結させることができないことだ。CSSフレームワークはCSSの文法の弱さを仕方が無いものとして捉え、作業の簡便さのためHTMLの部分的な編集を行うという選択をしただけだ。CSSフレームワークに対する「これじゃぁテーブルレイアウトの派生だ」などという批判はある意味正しいような面もあるとは思うし、そんな構成のCSSフレームワークもあるが、多くは苦渋の選択の結果または実装上の限界からの妥協だと思う。

しかし今、私達にはSass、そしてSassy CSS (SCSS)がある。

Sass

SassはHAMLという慣れるとものすごく生産性が上がるが慣れるまでがものすごく辛い文法でスタイルシートを記述し、CSSにコンパイルするというメタ言語で、結構昔からあった。Sassはとても魅力的なメタ言語でCSSに欠けている様々な機能を提供してくれる。

これらが主なものだが、他にもCSSを書いている時に「欲しいな…」と思うようなものや「なんでないの…」というような機能がある。特にミックスインの存在が大きく、これがあれば今のCSSフレームワークの機能はまずSassに移植することが出来るだろう。移植できさえすればSass内で完結することが出来るので、HTMLの更新は不必要になり、Sassで書いてCSSにコンパイルしデザインを更新という作業フローになる。開発それ自体のコストはあまり変わらないか若干増えるだろうが、更新されるファイルはCSSだけでHTMLは全く変化しないので、大量のHTMLファイルを再構築するという作業は必要ないし、動的にページを生成している場合でも訪問者のキャッシュが効くので転送量や負荷の削減にもつながるなどメリットの方が多いと思う。

一方で学習コストの高さというデメリットがある。僕は何度か触ったがどうしてもHAMLに慣れることが出来ず、その度挫折した。HAML自体が極端に難しいというわけではなく、CSSを意識しながらHAMLで書くというのはかなり難易度が高いと思う。

それを解決してくれるのがSass 3.0でサポートされたSassの新文法であるSassy CSS、つまりSCSSだ。

Sassy CSS aka SCSS

SCSSはSassの機能をCSS(CSS3のMedia Queriesなども含め)の文法とほぼ互換性がある形で再実装したもの。そのためCSSと同じような感覚で記述できる。

$black_half: rgba(0, 0, 0, 0.5);

@mixin logofont {
  font: {
    family: "lucida grande", "lucida sans", sans-serif;
    size:   300%;
    weight: bold;
  }
}

body {
  margin: 0 auto;
  width: 800px;

  header {
    h1 {
      @include logofont;
      border-bottom: 1px dotted $black_half;
    }
  }

  footer {
    border-top: 1px solid black;

    address {
      font-size: 90%;
    }
  }
}

こうCSSが書けたらいいのに! みたいな理想に近いと思う。セレクタを書く時も必ずネストしなければならないなどというルールはないので、既存のCSSをコピペしても大丈夫というのも非常に大きい。これをtest.scssとして保存し、コンパイルする。

$ sass --unix-newlines --scss test.scss test.css

出来上がったtest.cssはこうなる。

body {
  margin: 0 auto;
  width: 800px; }
  body header h1 {
    font-family: "lucida grande", "lucida sans", sans-serif;
    font-size: 300%;
    font-weight: bold;
    border-bottom: 1px dotted rgba(0, 0, 0, 0.5); }
  body footer {
    border-top: 1px solid black; }
    body footer address {
      font-size: 90%; }

文句なし。

このようにCSSをほぼ完全に継承しつつ、それらと互換性のある形で機能を追加しているところがSCSSの最大の魅力だ。

SCSSでCSSフレームワーク

実際にSCSSにCSSフレームワークを移植する例として、エントリのきっかけになったウェブページで触れられていたBlueTripを一部移植し、2カラムレイアウトを作ってみる。

2カラムレイアウトの作成

まずはベースとなるHTMLを作成する。以下のような簡潔なHTML5、極端に簡潔だが、2カラムレイアウトのサイトは多くの場合このようなHTMLになると思う。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Simple 2-Column Template</title>
  </head>
  <body>
    <header>
      <h1>Simple 2-Column Template</h1>
    </header>
    <div id="contents">
      <article>
        <h1>Article Title</h1>
        <p>Article contents</p>
      </article>
      <article>
        <h1>Article Title</h1>
        <p>Article contents</p>
      </article>
      <article>
        <h1>Article Title</h1>
        <p>Article contents</p>
      </article>
    </div>
    <aside>
      <h1>Side menu</h1>
      <ul>
        <li>sidebar menu item</li>
      </ul>
    </aside>
    <hr>
    <footer>
      <address>Site Footer</address>
    </footer>
  </body>
</html>

これに適用するBlueTripのCSSをSCSSに移植する。ミックスインで定義してしまうのが一番簡単だ。

@mixin column {
  float: left;
}

@mixin span-6 {
  width: 230px;
}

@mixin span-17 {
  width: 670px;
}

@mixin span-24 {
  width: 950px;
}

@mixin colborder {
  padding-right: 24px;
  margin-right: 25px;
  border-right: 1px solid #eee;
}

@mixin last {
  margin-right: 0;
}

@mixin space {
  background: #fff;
  color: #fff;
}

そしてこれらを@includeで参照してレイアウトを組み上げる。

body {
  @include span-24;
  margin: 0 auto;
}

body > header {
  @include column;
  @include span-24;
}

#contents {
  @include column;
  @include span-17;
  @include colborder;
}

body > aside {
  @include column;
  @include span-6;
  @include last;
}

body > hr {
  @include space;
}

body > footer {
  @include column;
  @include span-24;
}

CSSにコンパイルすると、

body {
  width: 950px;
  margin: 0 auto; }

body > header {
  float: left;
  width: 950px; }

#contents {
  float: left;
  width: 670px;
  padding-right: 24px;
  margin-right: 25px;
  border-right: 1px solid #eee; }

body > aside {
  float: left;
  width: 230px;
  margin-right: 0; }

body > hr {
  background: #fff;
  color: #fff; }

body > footer {
  float: left;
  width: 950px; }

こうなり、これを最初のHTMLに追加するとちゃんと2カラムレイアウトが出来上がる。ファイルにしてlink要素で読むべきところだが、ソースを見ればすぐ中身がわかるようにベタにコピペにした。またReset CSS他のBlueTripのベースとなる部分もコピペしてある。

ここではミックスインの定義から始めたのでCSSフレームワークを一から作るような煩雑さがあるが、移植はクラス名をミックスインにほぼそのまま変換するだけと非常に簡単だ。

clearfixとReset CSSの組み込み

少しCSSフレームワークとは話題がそれるが、clearfixやReset CSSが非常に便利に使えることにも触れておく。clearfixは通常以下のようにクラスで作り、必要な場所でクラス指定するというCSSフレームワークと同じようなアプローチで使用する。

.clearfix:before, .clearfix:after {
  content: "\0020";
  display: block;
  height: 0;
  visibility: hidden;
}

.clearfix:after {
  clear: both;
}

.clearfix {
  zoom: 1;
}

HTML5 Boilerplateではこのようなclearfixが使われている。これを例えばheader要素で使いたい場合は、

<header class="clearfix">

とする。これをクラス指定なしで書くとすると、適用したい要素のセレクタごとにclearfixをゴリゴリ書くことになり非常に面倒でかつ、見通しが悪くなる。SCSSではミックスインで定義してしまえば簡単に再利用でき、SCSSだけできれいに完結させることができる。

@mixin clearfix {
  zoom: 1;

  &:before,
  &:after {
    display: block;
    height: 0;
    visibility: hidden;
    content: "\0020";
  }

  &:after {

    clear: both;
  }
}

header {
  @include clearfix;
}

見通しの良いコードですよね?

同じようにReset CSSも簡単に組み込むことができる。単純にベタにコピペしても問題ないが、拡張された@importルールを使うと良い。

@import "reset";

SCSSに書くのはこの一行。必要なのは使いたいReset CSSを_reset.scssというファイル名で保存することだけだ(CSSの文法と互換性があるのでそのまま拡張子を変えるだけで大丈夫)。そうすればコンパイル時自動的に_reset.scssの中身をインラインに展開してくれる。

このように数多くあるCSSテクニックを簡単に組み込むことができるのもSCSSのさらなる魅力だ

カラム幅の変更

CSSフレームワークの移植の話に戻る。CSSフレームワークの良いところのひとつはレイアウトの変更がクラス名の変更で完了する手軽さだ。SCSSではミックスインが定義済みなら参照を変更するだけと、CSSフレームワークと同じ程度の作業で済む。

#contents {
  @include column;
  @include span-14;
  @include colborder;
}

body > aside {
  @include column;
  @include span-9;
  @include last;
}

このように参照を書き換えると、カラム幅の違う2カラムレイアウトになる。もちろんHTMLには一切手を加えていない。

カラムの入れ替え

BlueTripのようなCSSフレームワークでは主にHTMLの編集でレイアウトの変更を行うことになるため、例えばサイドバーを左に持ってきたいというような場合、HTMLを編集しコンテンツのブロックとサイドバーのブロックを入れ替える。さほど大変な作業ではないかもしれないが、CSSで出来ることなのになぜHTMLを編集しなければならないのか釈然としない人もいるだろう。SCSSでは既存のミックスインを派生させることでそういったことの実現もSCSS内で完結することが出来る。

@mixin flipcolumn {
  float: right;
}

@mixin flipcolborder {
  padding-left: 24px;
  margin-left: 25px;
  border-left: 1px solid #eee;
}

@mixin first {
  margin-left: 0;
}

最初のミックスインをフリップ(反転)させただけのミックスインを作成し、それを参照するように書き換える。

#contents {
  @include flipcolumn;
  @include span-17;
  @include flipcolborder;
}

body > aside {
  @include flipcolumn;
  @include span-6;
  @include first;
}

これで左サイドバーの2カラムレイアウトになる。もちろんこの程度のCSSのカスタマイズならCSSフレームワークでもCSSの定義を作成して、それを参照するようにHTMLでクラス名を編集をすれば良いので作業量自体はあまり変わらない。大きいのはSCSSがSCSS内で完結するということだ。

SCSSで完結することの意義

ではなぜSCSSで完結することが大きいのかという話になる。単純に作業量で言えばSCSSの方が若干多いくらいで、別に効率的ではない。大きいのはSCSSならばSCSSとCSSという親子のような存在のみを意識すれば良いことだ。CSSフレームワークではそうはいかない。HTMLとCSSだけで済むわけもなく、テンプレート言語のことも頭に入れる必要があるだろう。

そういう事が問題にならないケース、例えばバックエンド開発者とフロントエンド開発者が同じだったり密に連携できるようなケースならばCSSフレームワークはかなりの威力を発揮する。ここ数年でCSSフレームワークが大きく広まったのもそういった開発体制で作られるウェブサイトが多いことが一つの要因だと思う。

しかしそれでもCSSフレームワークはある種のジレンマがある。CSSフレームワークで定義されていないデザインへの変更のコストが非常にかさむことだ。色々用意されているプリセットの中では非常に簡単にカスタマイズ出来るのに対して、一歩その枠を超えようとすると途端に面倒さが跳ね上がる。貧弱な文法で既存のスタイルとの兼ね合いを意識しながらCSSを記述しなければならないということもそうだが、そう書いた上でCSSフレームワークとの整合性を保つために細かいクラス名を設定してそれをテンプレートで指定していくという作業がまた必要になるだろう。もちろん使用中のCSSフレームワークのことを無視して書いてしまうことも不可能ではないが、メンテナンスの困難なものが出来上がるか、最悪の場合は破綻してCSSフレームワークの使用を中止することになると思う。

SCSSでは様々なCSSテクニックをミックスインとして定義し、それらをSCSS内で適宜参照するようにしていくというのが基本的な使い方なのは前章で何となくわかったんじゃないかと思う。ミックスイン同士のバッティングというのは簡単に起こるが、参照側で適宜修正してやるだけで済む上、参照されたミックスインのみCSSコードとして出てくるので、CSSにコンパイルされた時にはそれらのバッティングが表に現れることはない。そしてこの章のタイトルのようにSCSS内で全ては完結する。CSSを定義して、HTMLを編集し、ウェブサイト全体を再構築する必要はない。SCSSで頑張ればそれをコンパイルしたCSSを更新するだけですべてが終わる。

これは作業量が減るというだけではなく、人的リソースの節約にもなると思う。HTMLで一部書かれているとは言えテンプレートは多くの場合バックエンドと密接に関わるもので、その編集にはそれ相応の知識が必要になるし、その関係の深さからバックエンド開発者が管理しているということもあるだろう。SCSSならば最終的にCSSの更新だけで終わるので、それらを考慮する必要性は少なく、独断で作業を行うことも可能だろう。そういった面でもSCSSで完結することの意義は大きい。

まとめ

CSSフレームワークを引き合いに出して長々と書いてきたが、Sass、そしてSCSSは960.gsやBlueprintにとって変わるものではない。それらとテンプレート・システム及びCSSの間を取り持つものだ。既存のCSSフレームワークを組み込んでしまい、SCSSフレームワークを作ることができるし、様々なところで公開されているCSSのエッセンスを取り込んだ自分なりのCSS製作環境を構築するというような利用の仕方もできる。つまりはこれこそが必要とされていたスタイルシート言語ということなのではないか。

また、HTML5の簡素だが強力な構造化する力に比して、CSS3が表現自体は進化したものの文法が全く進歩していないという現状にもうってつけなはずだ。ミックスインはベンダー拡張プロパティにも強いという特徴もあるのでCSS3の表現力とは非常に相性が良い。

@mixin border-radius($size) {
  -moz-border-radius: $size;
  -webkit-border-radius: $size;
  -o-border-radius: $size;
  border-radius: $size;
}

pre {
  @include border-radius(1em);
}

button {
  @include border-radius(4px);
}

こういう面でも今まさに求められている言語だと思う。

僕はSCSSに次のようなlink要素を書ける時代がきてもおかしくないと思ってしまうほどの可能性を感じる。

<link rel="stylesheet" type="text/scss" href="style.scss">

Sass、そしてSCSSを使ってみませんか?

最後に

ここまでで触れなかったことをいくつか最後に書いておく。

SassはCSSの文法だけを拡張する

CSSの表現に何かを追加するわけではない。SassならIE6でもposition: fixed;ができるようになるとかそういうものではない。CSSでできない表現はSCSSでもできない。

SCSSはCSSへの広範な知識が必要である

仕組み上CSSの知識がないとCSSフレームワークのコンバート程度でも戸惑う可能性はかなり高い。特にカスケーディングに関する知識はあったほうが良い。

Compass

Sass上のCSSフレームワーク。少し大仰すぎるような嫌いはあるが、インストールから使用まで今時のLL開発と同じ感覚で使用できると思うので、バックエンド開発者が使うならSass飛ばしてこっちでも良いと思う。

LESS

Sassと同じようなアプローチのもので、これもほぼ同じような機能を持つ。どっちがいいかを判断できるほどlessを使っていないのでわからない。