レンタルサーバー速度比較記事はこちら

Sassファイルから@charsetが削除されてしまうケースをまとめました。

Sassファイル(.scss)に @charset "UTF-8"; を書いていても、コンパイルして出力されたCSSファイルからは削除されていました。
Sassファイルの中身によって、@charsetがあったり無かったり。
出力オプションによってはBOMが付くケースも。

@charsetが消えるのはどんな時?
何が条件でそうなってしまうのか調査、整理しました。

コンパイル環境

node-sass 4.9.4
libsass 3.5.4

@charset削除はSassの仕様

ブラウザがCSSファイルの文字エンコードを判別するには複数の方法があり、@charsetは必須というわけでもないようです。

  1. ファイル先頭にある Unicode byte-order 文字 (BOM) の値
  2. Content-Type: HTTP ヘッダーの charset 属性、またはスタイルシートを送るのに使われたプロトコル内の同等のものから、得られた値
  3. CSS の @charset at-規則
  4. 文書参照で定義されている文字エンコーディングを使用。<link> 要素の charset 属性です。この方式は HTML5 で廃止されており、使うべきではありません。
  5. 文書が UTF-8 だと仮定する

引用元:@charset - MDN

それに基づいてSassコンパイラは、@charsetが必要ない場合は削除しているようです。
また、場合によっては出力ファイルにBOMが付くケースもあります。それも文字エンコード判別の仕様に準じたものですね。

参考 sassとcompassをアップデートしたらエラーが出た — 完結編MORILOG

@charset削除の挙動

最終的に出力されるCSSファイルにマルチバイト文字(日本語)が含まれているか否かで、@charsetの出力の有無、BOMの有無があることがわかりました。

どんな場合に@charsetが削除され、どんな場合は残るのか。
BOMが付くケースはどんな時なのか。
それを一通り整理してみます。

調査にあたりマルチバイト文字はコメントに残す形で確認しています。
コメントは // ではoutput-styleに関係なくSassファイル上にしか残らないため、/* */ または /*! */ を使用します。

expandedでコンパイル

--output-styleでexpnaded(非圧縮)でコンパイルした場合の挙動を確かめます。

マルチバイト文字を含まない場合

style.scss
@charset "UTF-8";

.header{
	width: 100%;
	height: 150px;
}
出力結果:style.css
.header {
  width: 100%;
  height: 150px;
}

@charsetは削除されていました。
僕が使っている秀丸エディタはBOMの有無も表示してくれるので、出力されたstyle.cssを見てみます。

BOM無しのUTF-8で出力されていますね。

マルチバイト文字を含む場合

style.scss
@charset "UTF-8";

/* 日本語 */
.header{
	width: 100%;
	height: 150px;
}
出力結果:style.css
@charset "UTF-8";
/* 日本語 */
.header {
  width: 100%;
  height: 150px;
}

@charsetは残っていますね
BOMを確認してみます。

こちらもBOM無しのUTF-8ですね。
@charsetが出力されているため、BOMを付ける必要が無いのでしょうね。

compressedでコンパイル

--output-styleでcompressed(圧縮)でコンパイルした場合の挙動を確かめます。

マルチバイト文字を含まない場合

style.scss
@charset "UTF-8";

.header{
	width: 100%;
	height: 150px;
}
出力結果:style.css
.header{width:100%;height:150px}

@charsetは削除されています。
BOMを確認します。

BOM無しのUTF-8ですね。

マルチバイト文字を含む場合

compressedでも日本語のコメントが残るように /*! */を使用します。

style.scss
@charset "UTF-8";

/*! 日本語 */
.header{
	width: 100%;
	height: 150px;
}
出力結果:style.css
/*! 日本語 */.header{width:100%;height:150px}

マルチバイト文字が含まれていますが、@charsetは削除されています。
BOMを確認してみます。

BOMありのUTF-8になっていました。
@charsetが出力されていないため、BOMを付けてエンコードを判別させているようです。

nested, campactの場合

--output-styleがnested、またはcompactの場合、どちらもexpandedと結果は同じでした。

出力パターンまとめ

ポイントとしてはSass(.scss)側にマルチバイト文字があるかどうかではなく、最終的に出力されるCSSファイル側にマルチバイト文字が含まれているかどうかで結果が変わります。

expanded, nested, compactの場合

CSSファイルにマルチバイト文字(日本語)が含まれている場合
→@charset が出力される。

CSSファイルにマルチバイト文字が含まれていない場合
→@charset が削除される。

BOMが付くパターンはありません。

compressedの場合

CSSファイルにマルチバイト文字(日本語)が含まれている場合
BOM付きファイルとして出力。

CSSファイルにマルチバイト文字が含まれていない場合
→BOM無しファイルとして出力。

どちらも@charsetは削除されます。
compressedの場合のみ、出力するファイルにBOMが付く場合があるので注意が必要ですね。

@charsetは書く必要すらない

@charsetをscssファイルに書いていなくても、マルチバイト文字が含まれていればコンパイル後のcssファイルに自動で@charsetが書かれていました。

style.scss
/* 日本語 */
.header{
	width: 100%;
	height: 150px;
}
expanded出力結果:style.css(UTF-8,BOM無し)
@charset "UTF-8"; ←自動で追加された
/* 日本語 */
.header {
  width: 100%;
  height: 150px;
}

マルチバイト文字が含まれていない場合は、@charsetは出力されません。

style.scss
.header{
	width: 100%;
	height: 150px;
}
expanded出力結果:style.css(UTF-8,BOM無し)
.header {
  width: 100%;
  height: 150px;
}

ほかのoutput-styleであっても同様です。
※@charsetをscssファイルに書いていても、二重に出力されたりはしません。書いても書かなくてもどちらでもいいってことですね。

まとめ

基本的にはSassコンパイラにお任せで、@charsetは意識する必要がなさそうです。

ただcompressed指定の場合、マルチバイト文字の有無によってファイルにBOMがあったり無かったりすることになるので、その辺を留意しておく必要はありそうです。
複数のCSSファイルを出力するような場合、BOMあり無しが混在するかもしれないのはちょっと気持ち悪いですね。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です