モードの変更

@importトリックの回顧

Advent Calendar
CSS

“昔々あるところに……。” CSS 昔話 Advent Calendarの 4 日目。

CSS の@import ルールは CSS 内で違う CSS ファイルをインポートすることができる機能だが、ブラウザによって宣言の解釈が異なっている時代があった。

/* 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 つ。

import.css は、HTML 側で link 要素で指定する。

<link rel="stylesheet" href="/css/import.css" media="screen">

sub.css は import.css 内の@import ルールで読み込む。

/* import.css */
@import "/css/sub.css"; /* url()でないのでIE4で読み込まれない */

main.css は sub.css 内で同じく@import ルールで読み込む。

/* sub.css */
@import url("/css/main.css"); /* シングルクォーテーションでないのでMac IE4.5で読み込まれない */

これを適用すると各ブラウザで読み込まれる CSS ファイルは

となり、link 要素と 2 段階の@import によって擬似的にブラウザ判定のようなことをしてリソースを制御するというわけだ。

このテクニックは個人的には「CSS ハック」ではなく「CSS トリック」だと思う。@import ルールの記述は valid なので CSS2 仕様に準拠したブラウザでは正常に動作し、準拠しているがゆえに未来のブラウザでも解釈が変わることはない。影響範囲がレガシーブラウザ以外に及ぶことはないので安心である。ということでこのテクニックを「@import トリック」と呼ぶことにしたい。今更感あるけど。

なぜ@import トリックが必要だったか

当時のレガシーブラウザ向け記述をファイル単位で分離するためもあったかと思うが、レガシーブラウザでスタイルを当てないことが最大の目的だったと思う。僕が関わっていたサイトでは主要ブラウザをメインターゲットに CSS が書かれていたが、IE4 ではそれらのスタイルを読み込ませると表示が大きく崩れてしまうとかフリーズしてしまうとかでまともに閲覧できない状況だった。IE4 だけに適用させるハックは当時は発見されておらず(おそらく今でも)、セレクタやプロパティーレベルのハックで対応することは不可能だった。そこで IE4 では CSS を当てない状態で閲覧可能にする選択肢が提案された。

当時の主要ブラウザであった IE6 向けのスタイル宣言では、背景画像指定などで多数の画像ファイルを読み込んでいた。スプライト画像の利用がほとんどなかった頃なので、IE4 を使っているプラットフォームでは当然それらのリソースファイルのロードも負荷がかかる。そのうえ表示が崩れるわ文字が見切れるわ固まるわで何も良いことがない。であればいっそ、CSS ファイルを読み込ませずに軽量な UA のデフォルトスタイルでサイトを見てもらおう、というわけだ。

この思想は@hiloki さんがブログに書いていたUniversal IE6 CSS の提案と同じものだ。僕は Universal IE6 CSS については知らなかったのだけど、ブログエントリを読んでとても懐かしく感じた。@import トリックを使って IE4 向けにスタイルを当てないという選択はやはり理にかなっていたのだなと嬉しくなった。

もちろん僕はその頃は新人だので、その選択をしたのはコーディングリーダーであり僕ではない

@import トリックのミソ

CSS の@import ルールは他の@ルールよりも前に、かつ@charset ルールの後に記述しなければならないと仕様で定められている。@import ルールを記述するとそのインポートファイルのスタイル宣言が先に読み込まれるので、ブラウザに解釈されるスタイル宣言はインポートする順番と逆の順になる。

  1. main.css のスタイル宣言
  2. sub.css のスタイル宣言
  3. import.css のスタイル宣言

main.css よりも sub.css、sub.css よりも import.css に書いたスタイル宣言がカスケーディングによって上書きされる。IE6 向けのスタイル宣言が IE4 向けのスタイル宣言で上書きされては本末転倒なので、_セレクタの詳細度によって制御する_。これが@import ハックのミソだ。

僕が関わっていたサイトでは import.css にはごく少量のスタイル宣言は書かれていて、そのセレクタは class や ID を使わないタイプセレクタによる記述だった。sub.css ではもう少し装飾を加えていたと思う。main.css では class セレクタや ID セレクタで書かれていたので、IE6 では import.css や sub.css のスタイル宣言の影響を受けない仕組みだった。main.css の読み込みまで到達できるブラウザのスタイリングを邪魔しないように詳細度の差をうまく使ったこのトリックは非常に賢いと感じる。

チームに入って最初にこの手法を見た時はその仕組みも詳細度のこともほとんど理解していなかったのだけど、今で言う reset.css に近い内容が import.css にも main.css にも書かれているのをコーディングリーダーに質問した時、丁寧に教えてくれたことを覚えている。より多くの人に情報を届けるべく、当時できる最大限のブラウザ対応を使命のように思っている人だった。僕より先に退職したが、今もどこかでウェブ制作に関わっているといいなと思う。

@import トリックや Universal IE6 CSS などを使ってレガシーブラウザではあえてスタイリングしないアプローチは、今後も残っていって欲しいと思っている。新しい Universal ** CSS 現れた時、僕は「より多くの人に情報を届ける」という、ウェブサイトの命題とも言えるそれを思い出すだろう。その言葉は、ウェブの進化の波に飲まれてレガシーとモダンの狭間という果てのない砂漠でカラカラになった僕に、あたたかな雨となって降り注ぐのだ。


ポエム締め楽しい。5 日目は@kojika17さんです。