メモログ

塵が積もって山とならないメモのログ

CSSのエラーの扱い方

How CSS Handles Errors - Tab Completionにて、CSSのパースで問題が発生した場合に、ブラウザがどのようにハンドリングするかの紹介がされています、という内容の紹介。仕様の4.2 Rules for handling parsing errorsや、4.1.7 Rule sets, declaration blocks, and selectorsで説明されているものと内容的には同じですけど。

At any given time, when parsing a CSS stylesheet, the browser is either trying to build an at-rule, a style rule, or a declaration (/property) inside of one of those.

CSSをパースするときに、ブラウザはat-rule(@mediaとか)、style rule(.classとか)、declaration(color:#ccc;とか)を構築する。(ので、それぞれの処理でエラーハンドリングがある)

declarationの場合は、エラーの発生した宣言は捨てられて、次の「;」が見つかるまで進む(it throws away the declaration, then seeks forward until it finds a semicolon that’s not inside of a {}, [], or () block.)。記事の下の方に下記のような例が出ていて、この例の場合、「radial-gradient」は認識できずにエラーとなるけど、そのときは「;」の部分まで無視されることになる。ので、後ろについているtransparentも無視される。

1
2
3
4
5
6
.foo {
color: white;
background: black;
background: radial-gradient(black, rgba(0,0,0,.5)) transparent;
}

仕様の方には、下記のような例が記載されている。

1
2
3
4
5
6
7
p {
color: green;
font-family: 'Courier New Times
color: red;
color: green;
}

font-familyの「'」が閉じられていないので、エラーとなるけれど、その場合、次の「;」までが無視されるようになる。なので、color:redの宣言も無視される。

at-ruleの場合は、エラーが発生したらat-ruleが及ぶ範囲全体が無視されるようになる。たとえば@modia{ … }みたいな場合は、{}全体が無視されて、@inport( … );みたいな場合は「;」までが無視される。

style ruleの場合は、エラーが発生したら{}ブロック全体が無視されるようになる。仕様には下記のような記載がある。

The selector (see also the section on selectors) consists of everything up to (but not including) the first left curly brace ({). A selector always goes together with a declaration block. When a user agent cannot parse the selector (i.e., it is not valid CSS 2.1), it must ignore the selector and the following declaration block (if any) as well.

CSS 2.1 gives a special meaning to the comma (,) in selectors. However, since it is not known if the comma may acquire other meanings in future updates of CSS, the whole statement should be ignored if there is an error anywhere in the selector, even though the rest of the selector may look reasonable in CSS 2.1.

特定のセレクタだけに問題があって、カンマで区切られた残りのセレクタは問題ない場合でも、カンマが将来のCSSでは(セレクタを分ける以外の)他の意味を持っている場合があるかもしれないから、記述全体を無視すべしと。

たとえば

1
2
3
4
5
6
.foo,
.bar:nth-child(2n+1):after,
.baz {
clear: left;
}

というCSSだと、nth-childが認識できないブラウザだと、.fooや.bazに対するclear:left;も行われなくなる。対応していないブラウザに対処するには、ruleをひとまとめにしないで、.foo,.bazと、bar:nth-child(2n+1):afterの2つに分ける必要がある。

1
2
3
4
5
6
7
.foo,
.baz {
clear: left;
}

.bar:nth-child(2n+1):after { clear: left; }

逆に言うと、vendor prefixがついたセレクタなどは、そのvendorでしか対応していないので、セレクタの中にそれを入れておけば、特定のブラウザだけに{}内の宣言を適用させるみたいなハックができる、ということになる。see also 不明なCSSセレクター - Weblog - hail2u.net

ひとつのセレクタがエラーである場合は、そのセレクタだけを無視するのが望ましいと思われるけど、上のようなハックなど、現状の挙動に依存しているサイトもあるから変更できないよねみたいなことが、最後の方に書かれていました。