CSSに抽象化の手法を導入したら便利じゃね?

CSSは、あまりにプリミティブすぎる、という話。

既存の問題点

(俺が知らないだけかもしれないけど)CSSは、基本的にHTMLタグの名前、クラス、擬似クラス等のセレクタ*1等で対象を指定して、その中にプリミティブなスタイルの指定を書き並べていく*2、というスタイル。

これって、ものすごく不便じゃない?少なくとも、反復作業が大嫌いなプログラマにとっては苦痛が生じる場面が多々ある。

例えば、ページを左右に2分割するようなレイアウトを考えてみる。右側が背景白、文字色黒で、左側が背景黒、文字色白にすることになった。その他のデザイン的な要素については、左右共に同じとする。このとき、スタイルシートはこんな感じになると思う。

div#page_right {
  width: 50%;
  float: right;
  background-color: white;
  color: black;
  (ここから共通部分)
}

div#page_left{
  width: 50%;
  float: left;
  background-color: black;
  color: white;
  (ここから共通部分)
}

単純にやろうとすると、共通部分がダブってしまう。DRYじゃない。コピペは悪。

共通部分を1箇所に追い出す方法として、自分が今思いつく方法としては、

  1. div {} で共通部分を指定する
  2. div#page_right と div#page_left を div#page の子要素として、div#page に共通部分を指定する(と、継承されて両方に適用されるはずだよね?)

しかし、1番目の方法だと、他のdiv を汚染することになるし、2番目の方法だとHTMLにページの論理構造と関係ないタグを追加することになり、エレガントではない。しかも、* {} とかで指定してある内容があったら、そっちが優先されてしまう。

ということで、問題点の洗い出しはできた。

問題点まとめ
  • DRYじゃない
  • HTML構造による継承を使うと、スタイル指定のプライオリティが低くなる

提案*3

スタイルセット、という抽象レイヤーを提案します。

スタイルセットとは
  • プリミティブなスタイル宣言の集合
  • styleset プロパティにスタイルセットの名前を書くことで、そのスタイルセットの宣言が適用される
具体例

ここでは仮に、スタイルセットの指定子は、スタイルセットの名前を<>で囲むことにすると、上に述べた左右分割のページだと

<page> {
  (共通部分)
}

div#page_right {
  styleset: page;
  width: 50%;
  float: right;
  background-color: white;
  color: black;
}

div#page_left{
  styleset: page;
  width: 50%;
  float: left;
  background-color: black;
  color: white;
}

というようになり、DRYな感じになって皆ハッピー。既存のCSSの文法とほとんど見た目が一緒なので、親しみやすさも抜群。

stylesetプロパティには、複数の値をとれるようにして、複数のスタイルセット間で宣言がダブっているプロパティがある場合は、名前を書き並べる順番にしたがって優先度をつければOKかと。

更なる問題点

Webの端っこにあって、エロいひとはあんまりみていないであろうこのブログにこんな記事を書いても、多分世界は変わらない。

その解決方法

  1. オレオレCSSテンプレートエンジンつくればいいんじゃね?
    • CSSの文法を拡張して、それをパースして既存のCSSの形で吐き出すテンプレートエンジン。というか、マクロエンジンとかいったほうが適切?
  2. みんながはてブすればエロい人がCSSテンプレートエンジン作ってくれるんじゃね?

追記

class属性に複数値を指定できる件

コメントをもらって調べてみると、どうやらhtmlのclass属性には、スペース区切りで複数のクラスを指定することができるらしい。大抵の場合、これで事足りるかもしれない。

しかし、極度のめんどくさがりのはやみずさんには事足りんのだよ。いまやHTMLを半自動生成しているはやみずさんには。

このエントリのHTMLのパターン手法を使うと、「section」という文字列から、

<div class="SectionContainer">
  <div class="SectionHeader">
  </div>
  <div class="SectionContents">
  </div>
  <div class="SectionFooter">
  </div>
</div>

というHTMLを生成する、というツールを使うことで、素早くHTMLを生成することができる。*4この方法で生成したHTMLをわざわざ変更するのは面倒だし、パターンの中に例外があると後々面倒なことが起こりそうで変更したくないのですよ。

つまり、class属性の複数指定ははやみずさんとってはまだ満足できないわけです。適度に勤勉な人は満足かもしれん。

セレクタが複数指定できる件

セレクタ複数指定できるのはしってたけど、共通部分を複数指定で書いて、非共通部分はそれぞれ書く、という方法は思いつかなかったよ。

div#page_right, div#page_left {
  (共通部分)
}
div#page_right {
  width: 50%;
  float: right;
  background-color: white;
  color: black;
}
div#page_left{
  width: 50%;
  float: left;
  background-color: black;
  color: white;
}

こんな感じですな。この方法を使えば、今気をもんでいる問題は解決できそうでつ。ありがとうエロい人。

気になる点をあえて挙げるとすれば、スタイルセットがオブジェクト指向の継承(親は子を知らない)*5に近いのに対して、セレクタ複数指定は共通なスタイルをもつセレクタを1箇所に書く方法(親は子を知っている)なので、慣れるまで感覚的に戸惑うかもしれない。多分慣れの問題。

セレクタが複数指定できる件の追記

セレクタが複数指定できることで、一応の解決策は見つかった。しかし、あとから実践してみると、この方法だとなんかやりにくい。↑に「多分慣れの問題」と書いたけど、

名前重要

という言葉がふと浮かんできた。抽象化のメリットは、プリミティブなものを一まとめにして名前を付けることができる、という点にあるんじゃなかろうか。セレクタを複数指定する方法だと、名前が付けられない点が、わかりにくさにつながっているんじゃないか。

例えば、「イケメンで、茶髪で、背が低くて、服をはだけた人を"ジャニーズ系"と呼ぶ」という抽象化を行う。すると、「AくんとBくんとCくんとDくんは、イケメンで、茶髪で、背が低くて、服をはだけている」というところを、「AくんとBくんとCくんとDくんはジャニーズ系」と言うことができる。後者のほうが分かりやすくない?

つまり、名前重要。抽象化は名前重要の原則に適っている(よね?)。コメントにある変数という考え方も、名前を付ける抽象化方法だし。

セレクタ複数指定で問題は解決できるが、抽象化したほうが人間フレンドリー。

*1:スタイルシート用語

*2:スタイルシート用語では、この一連のスタイル指定を"宣言"という

*3:あくまでふと思い浮かんだアイディアなので、そのうち変えるかもしれない。

*4:ツリー構造の効率の良い入力方法があれば、ツリーのデータから全体を構築するんだが、そこまで手の込んだことはやってない

*5:わりと慣れ親しんだ抽象化の概念