フレキシブルボックスのメロディ ― CSSの無知への傾向と対策

はじめに

「不良少年のメロディ ― 愛の鞭への傾向と対策」は、The Kinksのアルバム「The Kinks Present Schoolboys in Disgrace」の邦題です。高校時代への愛惜、詰め込み式教育への皮肉などを謳い、RCA在籍時の最後のロック・オペラとなった、(知る人は知っている類の)傑作です。

不良少年のメロディ ジャケット写真

ちなみに、上の画像をクリックしても、どこかのショッピングサイトに繋がることはないので、安心してください。反商業主義の当krzm.jpサイトは、いわゆるアフィリエイトという小遣い稼ぎは行っておりません。

それはそれとして、この記事では、Kinksのアルバム名とその精神にちなみ、ボックスレイアウトの傾向と対策を、当サイトのデザイン設計などに絡めて解説していきます。

第一幕
–CSS2までのボックスの段組み–

当サイトでは、ページの基本的な構成は、3段組みのレイアウトです。こうした段組みの各段は、一般的にはHTMLのdiv要素で作ります。div要素は、複数の文書段落(見出しや本文、リストなど)を囲んでグループ化する、範囲指定のための要素です。そして、「範囲を定義するほかには文書構造上の意味を持たない」という、意味を持たないことに意味がある哲学的な要素でもあるので、レイアウトのためのボックスは、この要素でよく作られます。

div要素による複数段落の範囲指定
div要素による複数段落の範囲指定

段落、CSS的表現では「ブロックレベル」ということになりますが、ブロックレベルの要素には、幅や高さを指定できます。段落がブロックレベルであるなら、それらを囲むものも必然的にブロックレベルであるべきなので、div要素もブロックレベルであり、幅と高さを指定すると矩形(ボックス、箱型)になります。そのようにして作ったいくつかのボックスを、横に並べれば、段組みが出来上がります。

div要素の横並び
div要素の横並び

しかし、ここでひとつの問題が生じます。ブロックレベル=段落です。世界が終わる果てには(たぶん)虚無しかありませんが、段落が終わるところには、必ず「改行」が付いてまわります。文章に改行を行うと、どうなるのでしょうか? 行が変わり、次の段落は下の行へ送られます。小学生の国語みたいな話で恐縮ですが、ようは、段落は縦に並んでいく、ということです。

ブロックレベル要素の改行表示
ブロックレベル要素は改行で縦に並ぶ

しかし、横組みの文書を段組みにする、といったら、それは段を横並びにしなければなりません。しかし、div要素などブロックレベルのものは、そのままでは縦並びであり、横には並んでくれないのです。このため、上下の階級構造を破壊し、自由・平等・友愛に基づく、ブロックレベルの横並びを実現する革命的行動が求められます。

まあ、そこまで大袈裟でもないのですが、CSS2までであれば、方法は3つあります。

インライン表示

ひとつ目は、ブロックレベルの質的な変換です。ブロックレベルの反対物として、「インライン」があります。これは、行内の文字列を意味します。段落内部の、文字そのもののことです。ですから、インラインは、その行幅が許す限り、改行されません。

インライン表示の図解
文字、単語、文など、段落中に含まれる文字列がインライン

そして、CSSでは、display:inlineを指定することで、ブロックレベルの要素であっても、インライン表示に切り替えられます。

インライン表示であれば、divでさえも横並びになるのです。しかし、インライン表示のものは、文字列ですから、各文字の幅や高さはあっても、その集まり全体に幅や高さを指定できません。つまり、幅や高さで表現される、「ボックス」ではなくなってしまうのです。ボックスを横並びにしたいがためにボックスではなくしてしまうのでは、どこか寓話的な本末転倒ですね。ですから、段組みのための方法としては、インライン表示は却下です。

絶対配置

ボックスがボックスであるままで、自己の存在を自己否定しないままで、横並びにするには、ちょとした強制措置が必要です。それは、絶対的権力による存在位置の定義です。もちろん、スターリンがクリミア・タタール人を強制移住させたような、非人道的措置とは違います。これは、positionプロパティで値にabsolute(絶対)を指定することで、その要素の配置場所をleft、right、top、bottomプロパティで数値(一般的にはピクセル値)指定し、いわば配置座標を決めてしまうというやり方です。

たとえば、幅100ピクセルのボックスの右隣に配置するなら、左からの距離を100ピクセルにすればよい(left:100px)わけです。

絶対座標指定の図解
絶対座標で左からの距離を指定する

この絶対配置を使えば、紙の上でのレイアウトと同じように、単なる横並びだけではなく、好きなところにボックスを並べていくことができます。幅100、300、100ピクセルの3つのボックスを並べるなら、下の図のようにleftプロパティを指定し、topプロパティは同じ値を指定します。

leftとtopプロパティ値による横並び
leftとtopプロパティ値を指定して横並び

絶対的存在の意思(配置)に身を委ねることは、宗教的恍惚すら感じられる崇高な手段かもしれませんが、宗教は民衆のアヘンであるように、この方法にも弊害というか、乗り越えられない壁があります。所詮、「神」も人間の創造物である以上、限界があるのです。これが神ならぬ紙の上であれば、絶対配置は絶対的に有効です。A4とかB5とか、面積の定まった世界では、そこに配置するボックスも、必然的にそのサイズが固定されます。なぜなら、紙の外に出てしまったら、最早存在は無に帰すからです。

しかし、Webページの場合、画面の表示サイズやブラウザのウインドウ サイズによって。ページ サイズは変わります。というよりも、サイズを特定することができません。そして、特に縦の場合、ページに入りきらないものは画面をスクロールさせて表示することが一般的です。つまり、ボックスの高さは、その内容に合わせて成り行きで決まる場合が出てきます。ケセラ、セラ~。もちろん、一定の画面サイズを想定して、その中に納まるように幅や高さを固定するもの(例えばページの「タイトル」)もありますが、ブログの記事のように、それぞれの文章量が異なる場合、幅は固定したとしても、高さは「自動」にして、成り行き任せにします。

もちろん、文章量に合わせて、その都度高さを指定してしまう方法もありますが、それには手間がかかります。「手間を惜しんでどうする」、というのは人として正しいかもしれませんが、Webページ設計上は現実的ではありません。

では、高さを自動にすると、どうなるのでしょうか? もちろん、レイアウトによってはそれでOKのものもありますが、例えば、段組みの下に次のボックスを配置する場合、絶対配置では問題が生じます。

段組みのボックスを絶対配置にしたとき、下のボックスが段組みの下に自動的に来てくれればよいのですが、CSSではそうは越後のちりめん問屋が卸しません。下のボックスは、上に上がってきてしまいます。これは、絶対配置にされたボックスの存在を、絶対配置ではないボックスが関知していないためです。positionプロパティで何も指定していない場合、デフォルト値は「static」になり、座標指定は効きません。いわば、絶対配置のボックスは、別次元か500兆年先の未来へ移動してしまい、staticのボックスからは見えなくなるのです。

position:static指定要素の配置
position値がstaticの要素は、絶対座標で配置した要素の下に移動してしまう

もちろん、ここで下のボックスも絶対配置に変更し、topの値を決めてしまえば問題は解決しますが、これも本末転倒です。そのtopの値は、何に基づいて決めればよいのでしょうか? 天の啓示でしょうか? いえ、そんなものはまやかしです。下のボックスのtop位置は、段組みボックスの高さに基づいて、決めなければなりません。

position:staticによる配置の問題点
下のボックスのtop値は、上中央のボックスの高さと同じになるが、その高さはautoなので特定できない

しかし、それらの高さは、成り行き任せです。その成り行きの果てにどんな高さになったのかを調べ、下のボックスのtopに指定することになります。それでは、段組みボックスの高さを固定するのと、同じことです。せっかくのケセラ、セラ気分も、吹き飛んでしまいます。

フロート配置

そこで、真打として登場するのが、フロート(float)配置です。これは、段組みにしたいボックスにposition:absoluteは使わず、floatプロパティを指定します。

本来、floatプロパティはテキストの回り込みを指定するためのスタイルです。例えば、行頭のimg要素などにfloat:leftと指定すると、その後ろのテキストはfloat:leftを指定した要素の右側に回り込みます。このプロパティを指定された要素は、leftの場合は左側に浮動して、その右側にテキストが回り込む領域を生み出します。

基本的なフロート配置の図解
行頭のimg要素にfloat:leftを指定すると、テキストは右側に回り込む

これをdiv要素のような段落に適用した場合も、後ろの段落は前の段落の下に潜り込みます。そして、後ろの段落内の文字列は、前の段落に対して回り込むようになります。

フロート配置したdiv要素の図解
div要素にfloatを指定した場合も、テキストは回り込む

もちろん、これだけでは、段落同士の横並びにはなりません。そこで、さらに下に潜り込んだ段落にもfloat:leftを指定すると、その段落自体も浮動するようになり、前の段落の隣に配置されます。ですから、横並びにしたい段落には、すべてにfloatを指定することになります。

横並びボックスの図解
横並びにさせたいすべてのボックスにfloatプロパティを指定

これが、フロート配置の”技法”です。ただし、あくまで技法であって、原理まで高められるものではありません。なぜなら、ボックスの段組みなどそもそもCSSでは想定されていなかったはずで、「このプロパティで裏技的にできるよ」というのが本当のところだからです。

さて、この技法は、あることを忘れると、上手に段組みを実現できません。それは、float本来の原理に関わります。先に解説した通り、「回り込み」こそ、floatの至上原理(市場原理、なんて不快なものではありません)です。floatを適用した要素の直後に来る要素は、float適用要素の下へ潜り込み、回り込みを実現しようとします。つまり、段組みの下のボックスは、段組みの下へ潜り込んでしまうのです。

非フロートボックスの配置
floatを指定していないボックスは回り込む

これを回避するため、一般的には、直後の要素に対し、floatの解除を指定します。そのために使うプロパティがclearです。float:clearではなく、clear:both(bothは両側の意味)といったように指定し、この要素が回り込みの誘いに乗らないようにします。

clearプロパティの図解
clear:bothを指定して回り込みを解除

まあ、世の中にあるほとんどの解説はこのように教唆し、最早アプリオリな原理として、floatとclearの使用がまかり通っています。しかし、天邪鬼な私としては、他の技法(あくまで経験に基づく技法であって、先験的な原理ではありません)を使っています。「against the trend」(流れに抗して)が、私の座右の銘のひとつですから。

その技法は単純なもので、「floatを適用した要素をdiv要素で囲んでしまう」、というものです。いわば、float適用要素を隔離してしまい、回り込み効果もその中に封じ込めるのです。結果として、隔離ボックスの後ろに来る要素には、clearを指定する必要がなくなります。

フロート隔離ボックスの図解1
floatプロパティを使用したボックスを隔離するボックスを追加する

この場合、div要素の構造がひとつ増えてしまいますが、後々、この入れ子構造が役立ちます。

ただし、CSSを正しく理解していないと、この入れ子構造の落とし穴に陥ります。実際、ただ隔離ボックスを作っただけでは、後ろに来る要素は期待される位置には表示されません。というよりも、ボックスの中身のテキストが、おかしな位置に表示されます。

フロート隔離ボックスの図解2
下のボックスの内容が、最も高さの短いボックスの直下に表示されてしまう

見た目はおかしいのですが、これは、CSSの仕様に従った表示なのでしようがありません。どのような仕様かというと、overflowプロパティの初期値に由来するものなのです。overflowとは、ある要素から中身がはみ出した部分のことを差します。つまり、はみ出し者のことです。そんな呼び方をすると西部劇みたいなので、下の図で概念を表現しておきます。

オーバーフローの概念図
風呂からあふれ出した水、すなわちオーバーフロー

さて、このオーバーフロー部分が、なぜ、子ボックスを囲んだ親ボックスに発生するのでしょうか? 子ボックスの高さをauto、親ボックスの高さもautoにしておけば、中身がはみ出すことはないように思われます。しかし、それは単なる思い込みでしかありません。CSSの仕様では、overflowプロパティの初期値が、visibleに指定されています。visibleは、「表示する」という意味です。

中身に文字列ではなくブロックレベル要素を持っている親ボックスは、高さがautoの場合、自らの高さを持たず、その中身ははみ出すことになります。そして、はみ出した部分が、overflow:visibleに従って、表示されます。しかし、親ボックスに高さがないため、その下にある要素の中身は、隙間を見つけて上がってきてしまいます。これが、レイアウトが崩れる要因です。

フロート隔離ボックスの図解3
子ボックスは、高さの無い親ボックスからオーバーフローした領域として表示(overflow:visible)される

この問題を解決するには、親ボックスが、中身に合わせた高さに伸びるようにしなければなりません。そのためには、overflowプロパティ値をhiddenにします。hiddenは、「隠す」という意味です。おいおい、隠したら、中身が消えてしまうのでは? ごもっともな疑問です。宗教は信じることで終わりますが、科学は疑うことから始まります。

overflow:hiddenを指定した場合、幅や高さが固定されているボックスでは、確かにはみ出した部分は消されてしまいます。しかし、幅や高さがautoの場合はどうなるのでしょうか? overflow:hiddenを指定すると、親ボックスは中身に合わせて伸びるようになります。ということは、はみ出す部分は存在しません。はみ出していないのだから、消されることはありません。つまり、overflow:hiddenは、中身に合わせて自身を伸縮させるスイッチをオンにするようなものなのです。

フロート隔離ボックスの図解4
親ボックスにoverflow:hiddenを指定すると、中身に合わせて親ボックスの高さが伸縮するようになる

おかしな理屈の積み重ねのようでもありますが、そこがコロンブスの卵的展開と言えるでしょう。コロンブスの”新大陸発見”について、私は偉業だとは認めませんが、卵の逸話は好きです。

こうして、float適用ボックスの親ボックスによる隔離と、親ボックスに対するoverflow:hiddenの指定によって、高さを固定しない段組みレイアウトが実現されます。

krzm.jp:not(so-called mobile friendly)