CSSの仕様では@charsetは先頭に置かれたものしか効果を発揮しない。最近は共にUTF-8でHTMLとCSSを書くだろうと思うので、あまり使われず、気にすることはもうあまりない。ただ何かしらの事情があって使う場合、カジュアルにファイルを連結してプロダクション用のCSSを作成すると、無意味な場所に@charsetが出てきて無駄が多くなる。

ちゃんと書かれていることを前提にすると、ブラウザーの処理の仕方と同じように先頭以外の@charsetを問答無用に削除しても良い。しかし、Normalize.cssのような最初に読ませる必要があるライブラリと@charsetが必要なCSSファイルを連結するケースではそれではダメになる。最初に見つけた@charsetを先頭へ移動させるというような形が一番マシだろう。

異なる@charsetが指定されたCSSファイルを連結する時におかしなことになるが、そのCSSは壊れてるので気にする必要はない。


CSSWringには結構前から@charsetを最初に見つけたものだけにしてそれを先頭へ持っていく機能がある。今まではグローバル変数として@charsetが既出かどうかのフラグを持たせて処理してたけど、以下のように書き換えた。

if (atRule.name === 'charset') {
  atRule.removeSelf();
  var first = atRule.parent.first;

  if (first.type !== 'atrule' && first.name !== 'charset') {
    atRule.parent.prepend(atRule);
  }

  return;
}

とりあえず削除して、もし親の最初のルール(aRule.parent.first)が@charsetでなかったら追加するという形にした。こうするとグローバル変数を使ったフラグ管理をしなくて済む。


このあたりのことを色々調べてて知ったんだけど、先日書いた通り@importでは直後の空白類を削除できるけど、@charsetでは削除できないことになっていた("@charset " {return CHARSET_SYM;}となっている)。文字コードを括る引用符を省略できるわけでもないし、なんでなんだろうか。歴史的な事情はありそう。