HOME

2016-4-30(Sat)

テーブル用 CSS をゼロから書くためのメモ

しかるべき仕様に基いて、ゼロから CSS を書けば、建前上はユーザーエージェントに左右されないでいつも同じように表示される表が作れるはずだ、と考えた。

どの仕様書を読めばいいのか?

HTML5

なにはともあれ、HTML5 とか、CSS とかの仕様を読む必要がある。

まず、HTML5 についてだ。困ったことに、HTML5 とは何かについては、それを作るのに参加した団体の間で、意見の相違があるようだ。

W3C は、2014年10月28日付けの勧告「HTML5」(https://www.w3.org/TR/html5/)こそが HTML5 だと言う。一方、WHATWG は、HTML5 というのは、現代的なウェブ技術の集りについて専門家が使う俗語なのだと言う(https://html.spec.whatwg.org/multipage/introduction.html#is-this-html5?)。

W3C は、WHATWIG にも文書があるよと言い( https://www.w3.org/TR/html5/ )、WHATWIG は W3C さんてばやめてって言ってるのに別の仕様を自分のとこに載せちゃうんだからって言ってる(https://html.spec.whatwg.org/multipage/introduction.html#is-this-html5?)。

WHATWIG のページ( https://html.spec.whatwg.org/multipage/introduction.html#history-2 )に「歴史」ってのがあったので、面白いからちょっと読んでみた。後半の内容は次のような感じだ。

XHTML ができてから、W3C は「これからは XML だぜー」て感じでやってきた。でも、2003年頃になって、XML のウェブ技術は、新規技術でしか進歩してなくて、HTML 的なところは進歩してないじゃん、てことにみんな気づいてきた。そこで、W3C は新しいワーキング・グループを立ち上げた。でもそれは、やっぱり XML な方針でいくってことには変わりがなかった。これに対して、Apple と Mozilla と Opera は、WHATWG という場を設けて、「俺たちはここで規格を策定するぜー」みたいになった。WHATWG は、後方互換性を大事にしたかったし、規格が実装と合わない場合には規格のほうの変更も辞さないという考えだった。それに、彼らは互いにリバース・エンジニアリングなんかしなくても、互換性が保てるようにしたかった。もっとも、そのためには、これまで別々の規格だった HTML4 や XHTML1、そして DOM2 HTML を HTML5 に含め、前よりも細部までいろいろ決めてやらなくちゃならないんだ。さて、W3C は 2006 年になって WHATWG と共同作業するワーキング・グループを作り、HTML5 の策定に参加することになったんだ。で、Apple, Mozilla, Opera は W3C が規格を自分たちのページに載せることを認めた。こうして何年も一緒に作業をしたけれど、2011 年になって、W3C と Apple, Mozilla, Opera は、どうも最終目標が違うぞってことになった。W3C は規格として HTML5 を完成させたかったんだけど、 Apple, Mozilla, Opera は未解決問題があるままの規格を完成させたって仕方がないと思ったんだ。それから現在に至るまで、WHATWG は Living Standard for HTML として HTML5 の改訂作業を続けているし、W3C はWHATWG が作った修正を自分たちのドキュメントにコピーしてるってわけだ。

お役所と業界団体のケンカみたいで困ったものだけど、とりあえずは W3C HTML5 ( https://www.w3.org/TR/html5/ )を参照して、もし必要があれば WHATWG ( https://html.spec.whatwg.org/multipage/ )を参照というあたりで何とか手を打つことにする。

CSS2

CSS については、HTML5 みたいなもめ事はないみたいだ。現在確定している限りでは、最新バージョンは 07 June 2011 の W3C 勧告「Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification」( https://www.w3.org/TR/CSS2/ )てことでよさそう。

そもデフォルトとは?

まず、一番簡単な表はどうしたら作ることができるかを考えてみる。面白いことに、一番簡単な表を作るというのは、そう易しいことではない。HTML はどうってことないが、問題は CSS だ。どこに貼り付けても正確にいつも同じように表を再現できる CSS を書こうと思ったら、ちょっとばかり勉強しなくてはいけない。

まずはじめに、CSS が HTML に出てくるような各属性に対して default と定めている値がある。これがどんなものかを目で見てみることにする。

<table id="cssdefault">
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>4</td><td>5</td><td>6</td></tr>
<tr><td>7</td><td>8</td><td>9</td></tr>
</table>

上のような HTML に、

<style type="text/css">
TABLE#cssdefault * { all: initial }
</style>

という CSS を適用してみる。TABLE#cssdefault * { all: initial } というのは、cssdefault というクラスに属する TABLE 要素とその子孫(*)において、すべての属性(all)を初期値(initial)にせよという指示である。(こうしてテーブルにクラス名をつけて、CSS の効果を限定しておかないと、このページのすべてのレイアウトがおかしくなってしまう)。

次がその結果だ。

123
456
789

おやおや! これは、表でもなんでもない。これが表として組まれない最大の原因は、CSS のデフォルトでは TR 要素の display 属性の値が inline になっているからだ。display 属性というのは、「ブロック」とか「インライン」とかいう言葉に関係する属性である(正確な定義は結構難しい。どんな属性値をとりうるかは https://www.w3.org/TR/CSS2/visuren.html#display-prop 参照)。この display 属性は、あまり目に触れることがないかもしれないが、CSS では極めて大切な属性だ。

「どうして CSS では TR 要素の display 属性のデフォルトが inline になっているのか」という疑問は、疑問自体が間違っている。というのは、CSS でいう default 属性というのは、属性ごとに 1 つ決まっているものであって、要素ごとに「この属性のデフォルト値はこれこれだ」というふうに決まっているものではないからだ。たとえば P 要素の display 属性も、TR 要素の display 属性も、display 属性であるからには、すべからくその初期値は inline なのである。

そうなると、われわれは要素ごとに各属性値を設定してやらなければ、とうてい表と呼べるものを見ることができないということになる。

とまれ、先ほどの例では TR 要素の display 属性を

<style type="text/css">
TABLE#tbdisplay * { all: initial }
TABLE#tbdisplay TR { display: table-row }
</style>

のように修正すると、

123
456
789

格段と改善された。

TR 要素の display 属性を含めて、われわれが html の表と認識するようなものを得るためには、少なくとも次のような display 属性の設定が必要だ。

table { display: table; }
caption { display: table-caption; }
col { display: table-column; }
tbody { display: table-row-group; }
tr { display: table-row; }
td, th { display: table-cell; }

これは私が勝手に書いたものではなくて、10.2 The CSS user agent style sheet and presentational hints ( https://www.w3.org/TR/html5/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints )の、10.3.9 Tables( https://www.w3.org/TR/html5/rendering.html#tables )というところに載っているのから、当面興味のある部分を拾ったものだ。

W3C が、各要素ごとの各属性値の「デフォルト」としてユーザ・エージェント(とりあえずはブラウザがそれだ)に用意してほしいと思っている値だと言っていいだろう。

この W3C の記述には、

table {
  box-sizing: border-box;
  border-spacing: 2px;
  border-collapse: separate;
  text-indent: initial;
}
td, th { padding: 1px; }
th { font-weight: bold; }

のようなことも書いてあるが、まあこれは無くたって我慢できなくもない。その他にも、色々あるが、まあそれは今は措いておく。

さて、とにかくユーザ・エージェントはそっくりそのままでないにしても、こうした属性ごとの属性値「デフォルト」を定めているが、これは「デフォルト」ではなくて、ユーザーエージェント・スタイルシートと呼ばれるので、このページでも以後そう呼ぶ。

3 種類のスタイルシート

ちなみに、CSS では、スタイル・シートの作り手は 3 つに分類されている。UA(ユーザ・エージェント)、author それから user だ。(https://www.w3.org/TR/CSS21/cascade.html#cascade)。UA スタイルシートは、すでに説明した。author スタイルシートというのは、私達が web page を作るときに読み込ませる、ページ内の、あるいは、外部ページの CSS だ。ユーザ・スタイルシートというのは、たとえばウェブページの読者がブラウザのメニューから設定を引っ張ってくると「背景色を変える」なんてのがあったとして、これで設定されるようなやつだ。

これらの設定が競合する場合、author、user、UA の順に強力だ。ただし、author スタイルシートと、user スタイルシートは、

p { text-indent: 1em ! important }

のような書き方ができて、その属性設定の優先順位を上げられる。

これらを含めるとは、強力さの順は次のようになる。

user(important) > author(important) > author > user > UA

important がつくと、user が author より強くなるなんてところが洒落ている。なお、author はどっちみち user より強力で、important な user が最強なんだから、author に important をつける意味はないように見える。これは、author スタイルシート内で、同じ属性の値を異るように設定しようとしたときに意味を持ってくるものだ。(参考 https://www.w3.org/TR/CSS2/cascade.html#important-rules )

リセット CSS

世の中にはリセット CSS というものがある。先程やったような、* に対して all: initial をしてしまうリセットの代わりに、読み込む CSS だ。たぶん主要ユーザー・エージェント間にある UA スタイルシートの差を埋めるために用いられるのだと思う。だから、主なユーザー・エージェントの間で違いがない display 属性の属性値なんかには触れないようだ。だから、リセット CSS でリセットしても、破滅的なレイアウトの崩壊は起こらない。そういう意味で、安全な範囲でのリセットをしてくれる。また、リセット CSS の利用者は、* や all: initial するより、ユーザー・エージェントによるレンダリングが早いと言う。

一方、リセット CSS を使う不利益もある。ユーザー・エージェントが使う UA スタイルシートが変更になると、使用するリセット CSS もそれに合わせて更新しなければならない。それに比べると * や all:initial を使うと UA スタイルシートが変更になっても影響を受けない。

私の小さな簡単な表

これで、やっと簡単な表を書くところまでこぎつけた。

たしかに、Firefox なり Google Chrome なり Safari なり Opera なり Internet Explorer なりといったユーザーエージェントが、どういうユーザーエージェント・スタイルシートを用意しているのかは、気になるところだ。それらが W3C の推薦するものと同じなのか、それらのユーザーエージェント間の異同はどうなっているのか。

だが、私はこのことに深入りするのはよしておく。私がやりたいのは、ユーザエージェント・スタイルシートに影響されない表づくりだ。そのためには、まず表におけるすべての属性を いったん default にしてしまい、それから自分で必要な属性値を与えていけばよいだろう。(じつは、まだこだけでは問題がある)

最初の表

123
456
789

このささやかな表は、次のようなコードによって表現されている。

<style type="text/css">
table#mytable01 * { all: initial } 
table#mytable01 { display: table; }
table#mytable01 caption { display: table-caption; }
table#mytable01 col { display: table-column; }
table#mytable01 tbody { display: table-row-group; }
table#mytable01 tr { display: table-row; }
table#mytable01 td, table#mytable th { display: table-cell; }
</style>
<table id="mytable01">
<tr><td>1</td><td>2</td><td>3</td></tr>
<tr><td>4</td><td>5</td><td>6</td></tr>
<tr><td>7</td><td>8</td><td>9</td></tr>
</table>

だが、これで完全だろうか? もし、initial値が inherit になっている属性が存在したら、all: initial という指定は、表の外部で設定した属性値を、表の内部に持ち込んでしまう。が、幸い inherit が initial 値な属性というものは、存在しないらしい。(参考 https://www.w3.org/TR/CSS2/propidx.html#q24.0 )

となると、あとの心配は initial 値が depends on user agent になっている属性だ。作った表の table 要素とその子孫要素の属性がすべて initial になると、ユーザー・エージェントの設定が生かされることになる。こうした属性は、color, font-family, quotes, voice-family だ。特に、color と font-family は表の見栄えにかなり影響がある。

フォントファミリー

font-family 属性は、family-name と generic-family のどちらかを指定する。複数指定することができ、その場合はカンマで区切る。generic-family のほうがとれる値は、

    'serif' (e.g., Times)
    'sans-serif' (e.g., Helvetica)
    'cursive' (e.g., Zapf-Chancery)
    'fantasy' (e.g., Western)
    'monospace' (e.g., Courier)

であり、列挙の最後にはこれを置いておくよう勧められている。(参考 https://www.w3.org/TR/CSS2/fonts.html )

font-family の initial 値はユーザー・エージェントに任されている。また、font-family 属性は継承される。

一応そうはなっているが、色々困った場面は生じる。たとえば、私の Linux 上にある Firefox は、メニューの「設定」から generic-font が指定された場合に使うフォントをセットできる。ところが、Firefox をインストールした時点では、この設定がうまく働いておらず、何でもかんでも sans-serif で表示するようになっていた。いわんや人のユーザー・エージェントをやである。

場合によってはフォントファミリーの指定に対しては匙を投げたほうが良い結果を生むかもしれない。どんな環境にしろ、ユーザー・エージェントがデフォルトに設定しているフォント・ファミリーがいちばん綺麗に見える可能性が高いのだから。

カラー

表を作るときには、フォントの色は color 属性によって決まる。そして、initial 値はユーザー・エージェント任せである( https://www.w3.org/TR/CSS2/colors.html#q14.0 )。だから table 以下のすべての要素で属性値を initial にしてもなお、フォント・ファミリー同様、スタイルシートに明記しておかないと表の見かけが環境に左右されることになる。

CSS では、カラーはキーワードか、RGB で定めることになっている。(参考 https://www.w3.org/TR/CSS21/syndata.html#color-units )。そして、キーワードは、aqua, black, blue, fuchsia, gray, green, lime, maroon, navy, olive, orange, purple, red, silver, teal, white, and yellow だとあり、それぞれの RGB が記されている。

また、OS のシステムカラーを拾えるとあり、https://www.w3.org/TR/CSS21/ui.html#system-colors に定義がある。

ちょっとマテ。もっと豊富なキーワードを見たことがあるし、実際それらは私の持っているブラウザで正しく表示されるぞ? どうやら、CSS3 のワーキンググループはもっといっぱい色名を用意している。( https://drafts.csswg.org/css-color/#named-colors )

ちなみに、CSS3 では、RGB のほかにアルファ・チャンネルを用意しているらしい。たとえば、色名に transparent なんてのを指定すると、RGB が 0,0,0 で、アルファ値も 0 になるとか。transparent は、なんか実装されているみたいだけど、アルファ値 0 の場合についてはまだ期待できそうもない。

そうそう。CSS の仕様では、RGB の記し方は

em { color: #f00 }              /* #rgb */
em { color: #ff0000 }           /* #rrggbb */
em { color: rgb(255,0,0) }      
em { color: rgb(100%, 0%, 0%) } 

のように書いてある。

あとは、CSS に色をどう記すかは、選択の問題だ。私は CSS2.1 を越えているのを承知で、いっぱいある色名を使いたい。かなり実装されてきているからね。

フォントサイズ

さて、table 要素とその子孫の属性をすべて initial にして、font-family 属性と color 属性とを書いたら、あとはつらつらと必要な属性を設定していけばよい。中でも重要な display 属性については、すでに触れた。

そこで、まず目立つところからいくと、フォントサイズである。

フォントサイズの設定について CSS の仕様を見てみる。

font-size のデフォルトは medium だ。medium が何を意味しているのかは、ユーザー・エージェントに任されている。

また、percentage とあるのは、親要素の font-size 属性値に対するパーセンテージ。

length については、次項。

長さ

font-size の表に出てきた、※ length というのは、

Relative units 
    em:  the 'font-size' of the relevant font
    ex:  the 'x-height' of the relevant font
Absolute length units
    in: inches — 1in is equal to 2.54cm.
    cm: centimeters
    mm: millimeters
    pt: points — the points used by CSS are equal to 1/72nd of 1in.
    pc: picas — 1pc is equal to 12pt.
    px: pixel units — 1px is equal to 0.75pt.
(参考 https://www.w3.org/TR/CSS21/syndata.html#length-units )

と定義されている。

さあ、これでフォント・サイズが自由に、そして頑健に設定できるぞ——と思うかもしれないが、もう少し続きがある。

pixcel という単位は一体何かということだ。上の表のように 0.75pt という設定がある一方で、pixcel には「0.26 ミリある物体を 71 センチ(28インチ)離れて見たときの視角」という定義がある。CSS には定義が 2 つあるわけだ。

デバイスがもし in,cm,mm,pt,pc にアンカーしているなら、1 in, 1 cm, 1mm, 1pt, 1pc は、世間でいうそれらに等しく、1 px は 0.75 pt である。一方、デバイスが px にアンカーしているなら、1 px は「0.26 ミリある物体を 71 センチ(28インチ)離れて見たときの視角」で定義される。そして、この場合も 1 px は 0.75pt であるから、今度は 1 cm は世間でいう 1cm にならない。

そして、我々はスタイルシートに、どこにアンカーするかを記すことはできない。

プリント用の CSS

ブラウザの印刷機能を利用した際、画面に見える姿と異ったものが印刷されることがある。これが、たいがいの場合、画面表示用の CSS と、印刷用の CSS が使い分けられているからである。たとえば、

(ここで力尽き候)

おわり

Follow @kabipanotoko