Sass 3.2 からは placeholder selector を使おう

この article は CSS Preprocessor Advent Calendar 2012 の25日目のエントリーとして書かれています(あれれ?!)。

ご挨拶

2012年も皆様の願い空しくクリスマスは中止されず、それどころか今年も元気に Santa Tracker がサンタクロースを追いかけ回していたわけですが、CSS メタ言語 lover の皆様におかれましてはいかがお過ごしでしょうか。僕個人は今年の師走は今までにない師走っぷりで、一番ヤバい時は某 b 社にいた20代の頃を思い出す感じでした。いつもだいたい忙しいピークは夏に来るんですが今年は違いましたね。でもひとまず何とか乗り切れました。そう、もちろん Sass があったからです。これがなかったらと思うと恐ろしいですね。2012 年の冬、僕を救ってくれたのは Sass でした。

ところで僕、去年も Less & Sass Advent Calendar のエントリーを書いておりまして、そこにも書いたのですが、実は私、2010年頃から勝手に Sass エヴァンジェリストを自称していまして、Sass を使い始めて4日目には並み居る Geeks の前で Sass のすばらしさを語るという蛮行に及んでいます。しかも JS 勉強会だったのに。まあそのへんの流れは去年のエントリーをお読みいただくとして。

去年のおさらい

その去年のエントリーは「Sass で Singleton を実現し、安心してクラスを再利用する」という一見何を言っているかわからないどころかなんか怪しいこと言ってる感じのタイトルだったのですが、これつまり、「あとで extend するかもしれないけど実際は使っていない class のが CSS に出力されるのウザいよね、こうすれば必要なやつが1回だけ出力されてハッピーだよ!」という内容だったわけです。概要を引用します。

CSSメタ言語を導入する目的の一つにリソース再利用性の実現というものがある。Sass の場合、mixin や extend によって実現している。特に extend によるスタイルの継承は強力である。

しかし再利用できるようなクラスをまとめたライブラリを import して利用すると、実際には使われていないクラスも出力されてしまい、ファイル容量が無駄に肥大する。使うクラスだけコピーするとか、使わないクラスは削除するなどの手動管理は手間がかかる。

このような問題があり、これを以下のように解決しようとしたわけですね。

そこでクラス自体を出力するような mixin を作れば、必要なクラス定義だけを include でき、不要な CSS コードを出力しなくて済む。しかし、extend したいクラスを必要になる度に include してしまうと、同じクラスが複数回出力されて CSS コードが肥大する。手動で管理するのはやはり手間がかかる。

あるクラスを何回 include しても必ず一回しか出力されないことを保証する singleton のような仕組みがあればこの問題を解決できそうである。

そこでまず、あるクラスを出力するための mixin を作り、それらに対して include ステータスフラグ用のグローバル変数を用意し、!default フラグによってその状態を制御することで singleton 機構を実現した。

placeholder selector の機能

というわけで、なんかちょっと難しそうです。実際は、一回慣れてしまえばテンプレートを使いまわすだけなんですが、コードもちょっと見慣れない感じでとっつきにくいのは確かです。しかしやるだけの価値はあったのです。当時は。ところが Sass 3.2 がリリースされた今はどのような解決が可能かというと…。

そこで %className という記述で placeholder class を作成し、これを extend すればよい。

なんと1文で終わってしまいました。逆に言いますと、去年僕が解決したかった問題というのはまさに placeholder selector が解決する問題であり、ネイティブに実装されていない機能を何とかがんばって実現した、という話だったのです。もしかしたら去年のエントリーを読んで「お前は何を言ってるんだ…」と思われた方も、「placeholder がやりたかったことを無理やり実現してたんだよ!」と言えば今ならわかっていただけるのかもしれません。

公式サイトの changelog から placeholder selector の説明を引用します。

Sass supports a new, special type of selector called a “placeholder selector”. These look like class and id selectors, except the # or . is replaced by %. They’re meant to be used with the @extend directive, when you want to write styles to be extended but you don’t want the base styles to appear in the CSS.

Sass は「placeholder selector」っていう特別なタイプのセレクターを新たにサポートしたよ。書き方は class とか id セレクタと同じような感じで、ただ # とか . の代わりに % を使うってだけだ。このセレクタは @extend から使うことを想定してるんだけど、extend する元のスタイルを出力される CSS には吐き出したくない、って時に使うんだ。

ということなのですが、これのうれしさ、伝わりますでしょうか。ある程度の規模とややこしさの Sass を書いてる方、もしくは汎用的に作ったものを使いまわそうとしたことがある方には伝わりやすいんじゃないかと思います。そうじゃない方にもこのうれしさを感してもらいたいので、今度は Chris Epsitein の blog エントリーからサンプルコードを引用してみます。

//これが
%clearfix {
  overflow: none;
  *zoom: 1;
}

aside, footer {
  @extend %clearfix;
}

#grid-container {
  @extend %clearfix;
}

//こうなる
aside, footer, #grid-container {
  overflow: none;
  *zoom: 1;
}

placeholder selector を使う事による利点

という感じで出力された CSS には clearfix という文字列は見当たりませんね。まあ clearfix のような汎用性のあるハックだったら普通のクラスにして .clearfix として使えるようにしておいてもいいんじゃないかとは思いますが。

というわけで機能的にまとめますと、以下のようになります。

これらの特徴はやはりスタイルライブラリを作って再利用するのにとても便利なものとなっています。その際に特にいいのは、「placeholder selector の名前は出力されない」という点です。まあ気の持ちようなのですが、名前が外に見えてしまうとあんまり恥ずかしい名前を付けられない、と思ってしまうじゃないですか。でも人に見られなければ結構気軽に名前をつけることができます。

これをアグレッシブに捉えると、従来とは違った考え方を導入することができます。それは「見た目そのまんまのクラス名をつける」ということです。つまり .boldred のような命名です。これは従来長らく CSS コーディングにおいては悪いことだとされてきました。これが悪手である理由は以下です。

しかし、視覚メディア向けにデザインし、それを記述するスタイルシートを書くという行為においては、視覚的分類を行いわないというのは不自然であり不可能です。にも関わらず従来の CSS の書き方のお作法はそれを極力避けてきたわけですが、もう楽になりましょう。見た目そのまんまの名前をつけてしまいましょう。ただしクラス名ではなく placeholder selector で。

これは決して茶化して言ってるわけではありません。そもそも @extend によるスタイルの継承という Sass の機能が素晴らしい理由は、視覚情報とその要素へのバインド設定を分けて記述できる点にあるのです。生の CSS ではこれらを分けることができないため、無理やりどっちかに寄せるか、やっぱり中途半端になるか、でした。しかし Sass の @extend によってそれを分離できるようになったのです。なのでこの利点を積極的に利用するために、視覚的スタイルを見た目ベース名前で管理することは理にかなったことなのです。そしてそれらのスタイルを共有ライブラリに入れておいても、実際に継承して使うまでは CSS には出力されないのでどれだけたくさん作っても問題ありません。

というように placeholder selector は CSS の記述をより本来的でかつ管理可能なものに出来る力があります。使い方も機能的には従来の .class を extend する方法となんらかわりありません。是非積極的に使ってその利点を感じてみてください。

というわけで去年のエントリーの内容はもう古くて意味がなくなったので obsolete ですそんなことありませんでした! 追記を参照してください。これからは placeholder selector を使いましょう! と言いつつも去年のエントリーに今も意味を見出すとすれば、それは曲がりなりにも paceholder selector の機能を去年の時点で実現できていた点です。これは Sass が基本的なプログラムの記述が可能だから実現したことです。もちろんネイティブに実装されないとできないこともいろいろありますが「プログラムできる」ことがもたらすパワーは大きいです。

このように CSS の記述のみならず、フロントエンド開発自体に大きなパワーと変化をもたらす CSS プリプロセッサー、それが導く未来はどんなものなのか? それについては CSS Nite LP26 で一緒に考えましょう :)

追記: Sass で Singleton はやっぱりまだ意味があった

上で「前に書いた『Sass で Singleton』はもうお払い箱だぜ!」的なことを書いたのですが、よく考えたらまだ意味がありました。placeholder selector だけでは汎用的に作られたクラスを使いまわす時に複数のファイルから重複インポートした場合、同じコードが複数回出力されてしまいます。これを避けるにはやはり読み込みステータスを管理するフラグを使う必要があるので、やっぱり共有ライブラリ的なものを作って使いまわす時には「Sass で Singleton 〜」の方法を使うといいでしょう。

実は Chris Epstine も全く同じ事言ってました。

If putting placeholder selectors in a sass module, follow this pattern to avoid selector duplication w/ mult imports: http://bit.ly/J7EZYT

$module-placeholders-defined: false !default;
@mixin module-placeholders {
  @if not $module-placeholders-defined {
    $module-placeholders-defined: true;
    %module-object { color: red; }
    %module-object:hover { color: blue; }
  }
}

@include module-placeholders;