@importトリックの回顧
- 公開日
- タグ
- Advent Calendar
- CSS
“昔々あるところに……。” CSS 昔話 Advent Calendarの 4 日目。
CSS の@import ルールは CSS 内で違う CSS ファイルをインポートできる機能だが、ブラウザによって宣言の解釈が異なっている時代があった。
- WinIE4 は@import ルールで url()を使わないと解釈できない
- MacIE4.5 は@import ルールでファイルパス指定をシングルクォーテーションで記述しないと解釈できない
/* WinIE4 は url() でないと解釈できない */
@import "/css/style.css" /* 無効 */
@import url("/css/style.css") /* 有効 */
/* MacIE4.5 ではシングルクォーテーションでないと解釈できない */
@import url("/css/style.css") /* 無効 */
@import url('/css/style.css') /* 有効 */
MacIE5 では url("/css/style.css")
は正常にインポートできる。
CSS2 仕様では@import ルールでファイルパスを指定する記述は2つある。
@import "style.css";
@import url("style.css");
上記のように @import
のあとにスペースを置き、クォーテーションでファイルパスを指定するか、url()
ファンクションを使ってクォーテーションでファイルパスを指定するかだ。クォーテーションはCSS2 における文字列の定義にしたがって、シングルでもダブルでも良い。
@import ルールでは文字列が指定されると url()
で記述されたかのように振る舞うとされている。よって、上記2つの記述は意味上等価となる。
IE4 がダブルクォーテーションだけのファイルパス指定を解釈できないのも、MacIE4.5 が url("file")
を解釈できないのも CSS2 仕様に反した挙動だが、ブラウザの実装として未対応なのか実装がミスっていてバグなのかはわからない。
ブラウザの解釈に差異があるところ、CSS トリックあり
この解釈の違いを利用してブラウザに読み込ませる CSS ファイルを制御していた、というのが今回のエントリの主なトピックだ。
主要ブラウザが IE6 の時代がきても、ニュースサイトやコーポレートサイトではシェアの低くなった前述のブラウザにも対応させる必要があった。その頃に僕が関わっていたサイトでは、@import の解釈の違いを利用してブラウザごとに違う CSS ファイルを読み込ませる手法が取り入れられていた。
用意する CSS ファイルは3つ。
- すべてのブラウザに読み込まれる base.css
- IE4 では読み込まれず、MacIE4.5 と IE5 以上で読み込まれる sub.css
- MacIE4.5 では読み込まれず、IE5 以上で読み込まれる main.css
base.css は、HTML 側で link 要素を使って指定する。
<link rel="stylesheet" href="/css/base.css" media="screen" />
sub.css は base.css 内の @import ルールで読み込む。
/* base.css */
/* url() でないので IE4 で読み込まれない */
@import "/css/sub.css";
main.css も sub.css 内の @import ルールで読み込む。
/* sub.css */
/* シングルクォーテーションでないので Mac IE4.5 で読み込まれない */
@import url("/css/main.css");
これを適用すると各ブラウザで読み込まれる CSS ファイルは下記となる。
- IE4:base.css
- MacIE4.5:base.css + sub.css
- IE5:base.css + sub.css + main.css
このように link 要素と 2 段階の @import によって擬似的にブラウザ判定のようなことをしてリソースを制御するというわけだ。
このテクニックは個人的には「CSS ハック」ではなく「CSS トリック」だと思う。@import ルールの記述は valid なので CSS2 仕様に準拠したブラウザでは正常に動作し、準拠しているがゆえに未来のブラウザでも解釈が変わることはない。影響範囲がレガシーブラウザ以外に及ぶことはないので安心だ。ということでこのテクニックを「@import トリック」と呼ぶことにしたい。今更感あるけど。
なぜ @import トリックが必要だったか
当時のレガシーブラウザ向け記述をファイル単位で分離するためもあったかと思うが、レガシーブラウザでスタイルを当てないことが最大の目的だったと思う。僕が関わっていたサイトでは主要ブラウザをメインターゲットに CSS が書かれていたが、IE4 ではそれらのスタイルを読み込ませると表示が大きく崩れてしまうとかフリーズしてしまうとかでまともに閲覧できない状況だった。IE4 だけに適用させるハックは当時発見されておらず(おそらく今でも)、セレクタやプロパティーレベルのハックは不可能だった。そこで IE4 では CSS を当てない状態で閲覧可能にする選択肢が提案された。
当時の主要ブラウザであった IE6 向けのスタイル宣言では、背景画像指定などで多数の画像ファイルを読み込んでいた。スプライト画像の利用がほとんどなかった頃なので、IE4 を使っているプラットフォームでは当然それらのリソースファイルのロードも負荷がかかる。そのうえ文字が見切れるなどの表示崩れも起こるし CSS ファイルを読み込ませても良いことがない。であればいっそ、CSS ファイルを読み込ませずに軽量な UA のデフォルトスタイルでサイトを見てもらおう、というわけだ。
この思想は@hiloki さんがブログに書いていたUniversal IE6 CSS の提案と同じものだ。僕は Universal IE6 CSS については知らなかったのだけど、ブログエントリを読んでとても懐かしく感じた。@import トリックを使って IE4 向けにスタイルを当てないという選択はやはり理にかなっていたのだなと嬉しくなった。
@import トリックのミソ
CSS の@import ルールは他の@ルールよりも前に、かつ@charset ルールの後に記述しなければならないと仕様で定められている。@import ルールを記述するとそのインポートファイルのスタイル宣言が先に読み込まれるので、ブラウザに解釈されるスタイル宣言はインポートする順番と逆の順になる。
- main.css のスタイル宣言
- sub.css のスタイル宣言
- base.css のスタイル宣言
通常であれば、main.css よりも sub.css、sub.css よりも base.css に書いたスタイル宣言がカスケーディングによって上書きされる。しかし IE6 向けのスタイル宣言が IE4 向けのスタイル宣言で上書きされては本末転倒なので、セレクタの詳細度によって制御する。これが@import ハックのミソだ。
僕が関わっていたサイトでは base.css にはごく少量のスタイル宣言は書かれていて、そのセレクタは class や ID を使わないタイプセレクタによる記述だった。sub.css はもう少し装飾を加えていたと思う。main.css には class セレクタや ID セレクタが書かれていたので、IE6 で base.css や sub.css のスタイル宣言の影響を受けない仕組みだった。main.css の読み込みまで到達できるブラウザのスタイリングを邪魔しないように詳細度の差をうまく使ったこのトリックは非常に賢いと感じる。
チームに入って最初にこの手法を見た時はその仕組みも詳細度のこともほとんど理解していなかったのだけど、今で言う reset.css に近い内容が base.css と main.css の両方に書かれていることをコーディングリーダーに質問した時、丁寧に教えてくれたことを覚えている。より多くの人に情報を届けるべく、当時できる最大限のブラウザ対応をすることを使命のように思っている人だった。僕より先に退職したが、今もどこかでウェブ制作に関わっているといいなと思う。
@import トリックや Universal IE6 CSS などを使ってレガシーブラウザではあえてスタイリングしないアプローチは、今後も残っていって欲しいと思っている。新しい Universal *** CSS が現れた時、僕は「より多くの人に情報を届ける」という、ウェブサイトの命題とも言えるそれを思い出すだろう。その言葉は、ウェブの進化の波に飲まれてレガシーとモダンの狭間という果てのない砂漠で乾涸びた僕に、あたたかな雨となって降り注ぐのだ。
ポエム締め楽しい。5 日目は@kojika17さんです。