小三ツ星インターフェイス

横並びグローバルメニューのCSSをどのように実現するか

はじめに

ウェブサイトを制作する上で、頻繁に使うテクニックが要素の横並びです。

グローバルナビゲーションや、メールフォーム、画像の回り込みやカラムの配置と、ウェブサイトのデザインに横並びは欠かせません。

一方、横並びはデザイン崩れを発生させやすいポイントでもあります。その目的に合致した横並びのテクニックを使う必要があります。ここでは、どのような場面でどういう技術を使うべきかについてまとめます。

ボックスモデルの再確認

横並びのテクニックを覚える前に、横並びのベースとなるHTML要素のボックスモデルについて改めて確認しておく必要があります。

ボックスモデルの基本

HTML要素のボックスモデル content width(content-box) height 相殺あり 相殺なし width(border-box) padding border margin

HTMLの全ての要素は、内側から順にcontent, padding, border, margin の4つの領域を持っています。

content

contentは、要素内のコンテンツがぴったり収まる領域です。
ボックス要素では幅はいっぱいまで占有しますが、インライン要素ではコンテンツが収まる最小限に収まります。
高さは、インライン要素では文字、あるいは画像の高さで、ブロック要素ではline-heigtが基本になります。

padding

paddingは洋服の肩パッドと同じように、contentからボックスを膨らませる役割を持っています。

デフォルトでは、widthで指定した値はcontentのもので、padding,border,marginを設定すると、要素の占有領域はwidthよりも広がります。要素を横並びする際、最初に引っかかるのがこれで、カラムが落ちる原因の一つです。

もっとも基本的な対策の一つは、box-sizing: border-box;を要素指定することでwidthの値をborderまでを含めたものに変更することです。

border

borderは、枠線をコントロールするプロパティです。box-sizingがデフォルトのcontent-boxの場合は外側に、border-boxの場合はwidthの内側に広がります。

IEでは、画像にリンクを設定すると、勝手にこのborderを設定し、青い枠線を表示するようになります。この問題を解消するためには、cssの最初に a img {border: none;}を指定します。

margin

marginは余白をコントロールするプロパティです。

このプロパティには独特の癖がいくつかあります。

左右に並ぶ要素に関しては、marginはpadding同様、左側の要素のmargin(margin-right)+右側の要素のmargin(margin-left)で余白を作ります。

ところが、上下に並ぶ要素に対しては相殺が発生します。
上または下の要素に対して、最低限どれだけ余白を作るかを決めるプロパティとして振る舞い、並んでいる要素の、大きいほうのmarginが余白として適用されます。

そのほか、隣接する要素に回り込み(float)が設定されている場合、marginは回り込みの設定されている要素に重複してしまい、余白を作れなくなります。

回り込み要素

width: 200px; height: 200px; float: left;

pタグにmargin: 40px;を設定。
上下と右側には40pxの余白があるが、左には余白が無い。

div 要素(height: 150px; margin: 40px;)

この問題を避けるため、原則として回り込みを指定している要素にmarginを指定しましょう。

回り込み要素

width: 200px; height: 200px; float: left;
margin-right: 40px;を設定

pタグにmargin: 40px;を設定。
上下と右側には40pxの余白があるが、左には余白が無い。

div要素 ( height: 180px; margin: 40px;)
要素自体は回り込み要素に重複しているが、コンテンツと回り込み要素の間には余白ができている。

どうしてもそれができない場合は、以下の対策を行います。

前の要素に回り込んだ上で、marginを効かせたい場合は、回り込む要素にもfloatを指定します。

前の要素に回り込ませない場合は、clearを指定します。

回り込み要素

width: 200px; height: 200px; float: left; margin-right: 40px;

pタグ margin: 40px;を設定。
float: left;も設定。
幅が100%から最小限に縮められている

div要素 (height: 180px; margin: 40px;)にclear: both;を適用。

なお、floatを適用した場合、適用した要素は、それがブロック要素であったとしても、幅はコンテンツを収める最小限まで縮みます。floatを適用する一方、後続の要素を回り込ませないようにするには、幅いっぱいまで占有するようにwidthの値を指定するか、floatした要素にclearfixを適用するか、後続の要素にclearを指定します。

ブロック要素とインライン要素

ブロック要素とインライン要素の違いは、デフォルトで幅をいっぱいに取るか、中のコンテンツの幅でとどめるかの違いになります。役割としては、ブロック要素が箱、インライン要素は文字となります。

HTMLの要素は全てデフォルトでこれら2つのいずれかとなっています。インライン要素は、a, span, img, strong, q など、文字列の中で用いられることが前提の要素です。ブロック要素はそれら以外のほぼ全て。h1~h6の見出し要素、div, p, hr, ul, ol, li などが頻繁に使われます。ブロック要素は幅や高さを任意に設定できます。

CSSで、display: inline-block; を指定すると、少しユニークな性質を持たせることができます。inline-block を指定された要素は、インライン要素のように行内に収まりますが、改行の際はその要素が一かたまりで改行され、中の文字列が途中で改行される動き方はしません。また、幅や高さをブロック要素のように指定することができます。グローバルナビやフッターのリンク、タグクラウドなどに用いると、リンクの文字列が途中で折り返されることが無くなり、キーワードが読みやすくなります。

横並びのマークアップ

グローバルナビなどのリンク、メニューなどをマークアップする際は、文書構造を明確にするため、通常はリスト要素を用います。

<ul>
  <li><a href="#">menu1</a></li>
  <li><a href="#">menu2</a></li>
  <li><a href="#">menu3</a></li>
  <li><a href="#">menu4</a></li>
</ul>

これをブラウザで表示させるとこうなります。

各要素のコンテンツエリアを確認するため、背景色をつけます。ulにピンク色を、liに水色を、リンクには緑をつけるとこうなります。

li要素のmargin,paddingはいずれも0です。
ulにはデフォルトでpadding-leftがあるため、li要素の左側にulの領域が見えています。
また、liはブロック要素なので幅いっぱいまで領域を占有しています。a要素はインライン要素のため、文字の幅分しか幅を占有しません。
a要素の上下にある隙間は、1行の高さを1.6にしているために発生しています。

リストの行頭の黒丸は横並びの際の邪魔になるので、ul {list-style: none;}で消してしまいましょう。

ulのpadding-leftはulの高さが見えるように、このままにしておきます。これで横並びの下準備ができました。いよいよ、横並びの実践を行います。

横並びを実現するCSS

liが縦に並ぶのは、それがブロック要素で回り込みも設定されていないからです。後ろのli要素を横に回りこませるには、li要素をブロック要素からインライン要素に変更する、ul,liをテーブル要素に変更する、回り込みを設定する、のいずれかで実現できます。

それぞれの特徴を確認しましょう。

li要素をinlineにする

ブロック要素であるliに対してdisplay: inline;を指定します。

liがインライン要素になったことから、中の文字列をぴったり収める範囲にサイズが縮みました。line-heigtによる余白はliにはなく、その親のブロック要素であるulに対して利いています。

ちなみにulもインライン要素にしてしまうとこうなります。

line-heigt: 1.6;がまったく無くなってしまいました。line-heightはブロック要素において有効であることが分かります。

さて、他に気になるのはli要素の間に中途半端な余白が発生していることです。li要素にはmarginもpaddingもtext-indentも0なので、CSSではコントロールできません。

この余白は、</li>の後で改行していることで生じています。つまり、全く見えませんが、ここには「改行コード」という見えない文字が1文字入っています。そのため余白が生じています。

何だ面倒臭いな、と思うかもしれませんが、ブラウザはpタグなどでマークアップしていない、裸のテキストも表示するようにできています。一方でHTMLの仕様では改行はbrやpタグを用いることになっており、テキスト内の改行コードでは改行を実行しないことになっています。それが、改行コードという不可視の文字をわずかな空白として扱わせている原因です。

これはdisplay:inline;に限らず、pタグ内でも同じ現象が生じます。

●この1行には改行コードはありませんが、




1



1












上の2行を比べると、下の方が文字間隔が広いのがわかります。間に改行コードが入っているせいです。

これが、liをインライン要素にして横並びにしたときに生じる隙間となっています。

次のようにHTMLコード側で改行をなくす対応をとることである程度解消はできます。

<ul>
  <li><a href="#">menu1
  </a></li><li><a href="#">menu2
  </a></li><li><a href="#">menu3
  </a></li><li><a href="#">menu4</a></li>
</ul>

リンクの隙間は無くなりますが、やはり改行コード分、文字に隙間が発生しています。完全に無くすためには全て1行で表示しなければならず、コードがかなり読みにくくなります。

<ul>
  <li><a href="#">menu1</a></li><li><a href="#">menu2</a></li><li><a href="#">menu3</a></li><li><a href="#">menu4</a></li>
</ul>

多少トリッキーではありますが、CSS側での対処も不可能ではありません。liを囲んでいるulの文字サイズを0にして、liのほうで文字サイズを復活させる、という方法です。

ul {
  font-size: 0;
}
ul>li {
  font-size: 16px;
}

最後に、これをグローバルナビっぽく均等に割り付けるため、liに幅を設定してみます。

<ul class="inline-menu">
  <li><a href="#">menu1</a></li>
  <li><a href="#">menu2</a></li>
  <li><a href="#">menu3</a></li>
  <li><a href="#">menu4</a></li>
</ul>
.inline-menu {
    background-color: #FCF;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0;
}
.inline-menu li {
    background-color: #CFF;
    display: inline;
    font-size: 16px;
    width: 25%;
}
.inline-menu li a {
    background-color: #0F0;
}

見ての通りうまくいきません。

インラインに置きつつ、幅や高さを付けたい場合は、inline-blockを使います。

inline-block

まずは、liにinline-blockを指定しただけの状態を確認します。

inlineにしたときとは異なり、li要素に高さが残っていることが分かります。この高さはline-height:1.6;によって生じています。

また、inlineのとき同様、改行コード分の隙間があります。これらを解消し、均等に分割してみます。

<ul class="inline-block-menu">
  <li><a href="#">menu1</a></li>
  <li><a href="#">menu2</a></li>
  <li><a href="#">menu3</a></li>
  <li><a href="#">menu4</a></li>
</ul>
.inline-block-menu {
    background-color: #FCF;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0;
}
.inline-block-menu li {
    background-color: #CFF;
    display: inline-block;
    font-size: 16px;
    width: 25%;
}
.inline-block-menu li a {
    background-color: #0F0;
}

liの幅を4分割となる25%で設定したところ、きれいに収まりました。

リンクの領域が狭いので、これをLi要素いっぱいまで広げます。高さ、幅を指定できるようにaタグをブロック要素にして、幅を100%にするとli内がすべてクリック可能になります。

.inline-block-menu li a {
    background-color: #0F0;
    display: block;
    width: 100%;
}

なお、li要素を、例えば40pxなどと指定した場合、a要素の高さが不足します。

この場合は、a要素に高さ100%を指定すると解決します。

なお、親要素に高さが指定されていない場合にheight:100%;を指定してしまうと、表示領域に対する高さとなるので気をつけましょう。

liに枠線を設定する場合は、box-sizing: border-box;を指定しないとカラム落ちします。

li {
    background-color: #CFF;
    display: inline-block;
    font-size: 16px;
    width: 25%;
    height: 40px;
    border: 1px solid #666; ←枠線だけ追加
}

これはデフォルトのcontent-boxでは枠線の分、liの占有幅が25%をわずかに超えるためです。liのbox-sizingにborder-boxを適用するときれいに収まります。

さて、中の文字をliのボックスの中心に配置したいところです。

左右の関してはtext-align: center;で良いのですが、上下の位置の調整は困難です。文字が1行で収まるのであれば、line-heigt を高さと同じ値にする、という方法があります。

display: table 及び table-cell

display: table; と display: table-cell; は、要素を表組みにするCSSです。

<ul class="table-menu">
  <li><a href="#">menu1</a></li>
  <li><a href="#">menu2</a></li>
  <li><a href="#">menu3</a></li>
  <li><a href="#">menu4</a></li>
</ul>
.table-menu {
    background-color: #FCF;
    margin: 0;
    padding: 0;
    list-style: none;
    display: table;
}
.table-menu li {
    background-color: #CFF;
    display: table-cell;
    height: 40px;
    border: 1px solid #666;
}
.table-menu li a {
    background-color: #0F0;
    display: block;
    width: 100%;
    height: 100%;
}

横幅をいっぱいにして均等割りにするには、display:table;を指定している要素に width: 100%; と、 table-layout: fixed; を指定します。

table要素では、box-sizing: border-box; を指定しなくてもカラム落ちする心配がありません。

一方、liの領域全体をクリックできるようにするため、a要素をブロック要素すると、vertical-align: middle;が機能しません。文字列を中心に配置する手間はinline-blockを用いる方法と変わらないといえます。

また、レスポンシブに対応する(一定の画面幅以下では横並びを解除する)には、table指定を解除する必要があります。li要素にmin-widthを設定すれば勝手に横並びを壊してカラム落ちしてくれるinline-blockより少しコーディングの量が増えます。

float: left;

もっとも古くから用いられ、関連テクニックが熟成されているプロパティです。

li要素にfloat: left; を指定すると以下のようになります。

<ul class="float">
  <li><a href="#1">menu1</a></li>
  <li><a href="#2">menu2</a></li>
  <li><a href="#3">menu3</a></li>
  <li><a href="#4">menu4</a></li>
</ul>
.float {
    background-color: #FCF;
    list-style: none;
}
.float li {
    background-color: #CFF;
    float: left;
}
.float li a {
    background-color: #0F0;
}

 floatを用いる場合に注意すべきところは、何もしないと親要素のulから高さが消えることです。親要素から高さが消えることで、floatを設定したliの後の要素が、横に回りこめるようになります。(この文もまた、liに回り込んでいます。)

このままだと結構面倒なことが頻出するので、このメニューの後の要素は回り込まないようにしたいところです。

方法は何種類かあります。

1. ulに高さを設ける

ul要素は、100%の幅を持っているため、高さを指定すると後続の要素が回りこむ余地が無くなります。以下はulに40pxのheightを設定したばあいの表示です。

メニューの高さが可変でない限り、この方法を用いることができるでしょう。

2. ulにclearfixを適用する

ul:afterの擬似要素で回り込みを解除しています。

この方法の利点は、liの高さが変化してもそれにulの高さが追従してくれることです。

後続の要素にclearを適用する

ulやliではなく、後続の要素で clear: left; や clear: both; を適用することで回り込みを解除します。ulの高さも無くなったままです。

この方法はfloatが設定された要素の後にあるというだけで、内容的には関係のない要素に変更を加えなければならないという点であまり合理的ではありません。ulの後に続くものが何であれ回り込ませない、というのであれば、その処理は根本原因であるul側で行うべきです。

特定の機能を持ったパーツの中で処理は完結すべきであり、周辺に後処理を押し付けるべきではありません。そうしないとメンテナンスがどんどん複雑になってしまうでしょう。


さて、メニューを幅いっぱいに均等割して文字列を中心にそろえてみましょう。

ulのpaddingを0にして、liの幅を25%にすると共に、box-sizingをborder-boxにします。これで幅の均等ぞろえができます。

また、liにdisplay: table; を、aにdisplay: table-cell;を設定し、aにtext-align: center; とvertical-align: middle; を適用すると、中央揃えが簡単にできます。

また、回り込みの解除のためにulにclearfixを適用しています。これを省くと、ulに高さがないため、後続の要素のmargin-topがメニューに重複してしまい思い通りに行きません。

(下の例ではliにheigt:40px;も設定しています。)