水平メニューを作る CSS のスタイルまとめ

ウェブサイトでよく見かける「横方向に並ぶメニュー」の作り方についてまとめてみます。

記事執筆時点では、主流の横方向に並ぶメニュー(以後「水平メニュー」)の作り方は大きく分けて 4 つあります。

  1. float を使う方法
  2. inline-block を使う方法
  3. table レイアウトを使う方法
  4. flexbox を使う方法

以下それぞれの実装方法( HOW )について述べていきますがまずその前にそれぞれのメリット/デメリットと使いどころ( WHEN )について確認してみたいと思います。

各アプローチのメリット/デメリット

1. float を使う方法

float を使う方法のコードは次のようなイメージです。

.menu {
  overflow: hidden;
}
.menu-item {
  float: left;
  width: 25%;
}

float アプローチのメリットとデメリットは以下のとおりです。

メリット:

  • ◯ 要素と要素の間に隙間が空かないので指定した幅( 100% など)にぴったり合わせられる。
  • ◯ min-width を決めて幅がしきい値よりも狭い場合に末尾の要素を 2 行目に送ることができる。

デメリット:

  • × 改行が発生するとその要素だけ高さが大きくなり、また縦方向の中央寄せもできないので、セル内での改行が発生しないよう調整が必要。
  • × 要素の数を変えると width を変える必要があるので必ず CSS を変える必要が出てくる。

使いどころ:

float 方式は幅を 100% にして使う際に便利なので ヘッダー内のメインメニュー などに使うとよいでしょうか。 ヘッダー内の水平メニューは画面幅が狭いときには縦並びにすることがパターンとして多いので、縦並びに変えたい場合にも floatwidth を変更するだけで済むのもこの場合のメリットです。

2. inline-block を使う方法

inline-block のコードはこんな感じ。

.menu {
  text-align: center;
}
.menu-item {
  display: inline-block;
}

inline-block アプローチについては次のとおりです。

メリット:

  • ◯ レスポンシブでの中央寄せがやりやすい。
  • ◯ 幅が足りない場合は、行内での縦方向の中央揃えを維持しながらスムーズに改行してくれる。

デメリット:

  • × 要素間にスペースが空くので厳密に幅を指定できない(対策はあるが、どのモダンブラウザでも問題なく解決できる方法がなさそう)。

使いどころ:

inline-block 方式は全体のラッパーを中央寄せにするとかんたんに可変幅で中央寄せができるので、 フッター内の中央寄せメニュー に向いています。 上下のマージンを適切に設定しておけば、メディアクエリをほとんど使わずひとつのスタイルですべての画面に対応することも可能です。

3. table レイアウトを使う方法

.menu {
  text-align: center;
}
.menu-item {
  display: inline-block;
}

続いて table パターンについてです。

メリット:

  • ◯ 幅 100% ぴったりにしながら( inline アプローチの課題を解決しながら)、かつ、セル内での改行が発生しても縦中央揃えを崩さない( float アプローチの課題を解決する)メニューが作れる。
  • ◯ 幅に余裕がある場合は要素の数が多少変わっても CSS を変える必要がない。

デメリット:

  • × float や inline のように改行が発生しないので、幅が狭くてセル内改行が想定以上に発生しないよう調整が必要。
  • × table 特有の問題が起こる。例えば CSS の仕様だとテーブルのセルに position: relative が使えない(効くブラウザが多いけれど)。

使いどころ:

table レイアウトは float 方式と同様、幅 100% で使うのに向いているのでこちらも ヘッダー内のメインメニュー に向いているでしょうか。 float 方式とのちがいとして、画面幅が狭いときにも改行が発生しないので、画面幅が狭いときの縦並びスタイルは別途書く必要があります。 逆に、メニューの項目数の変更には強いので(多少の変更であれスタイル変更が不要なので)、サイト管理者がある程度柔軟にメニューを変更したい場合などには特に便利です。

table 方式は昔の悪しきテーブルレイアウトを連想させるために一見印象がよくありませんが、うまく使うと float などよりもずっとエレガントに書けるので積極的に使っていくのがよいかと思います。 ただし、セルに position: relative が原則使えないようなので、 :before:after の擬似要素でアイコンなどを挿入しようと考えている場合には a タグだけで十分か注意する必要があります。

4. flexbox を使う方法

.menu {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  justify-content: space-between;
}
.menu-item {}

flexbox は本稿執筆時点でまだ仕様が確定していないような感じで、古めのブラウザ対応なども考えると安心して使いづらい状況なので、今回は割愛します。 主流のブラウザで問題なくサポートされ安心して使えるようになるまでもうしばらく待つ必要がありそうです。

各アプローチのメリット/デメリットと使いどころについてざっと見てきました。 続いてそれぞれの実現方法について見てみたいと思います。

各アプローチのサンプルコード

各アプローチのサンプルコードをあげていきます。 細かな部分はデザインによって異なりますので、装飾のない必要最小限のスタイルだけを書いていきます。

1. float を使う方法

HTML:

<ul class="menu menu-float">
  <li class="menu-item"><a href="" class="menu-link">ホーム</a></li>
  <li class="menu-item menu-item-active"><a href="" class="menu-link">アバウト</a></li>
  <li class="menu-item"><a href="" class="menu-link">serviceserviceserviceserviceservice</a></li>
  <li class="menu-item"><a href="" class="menu-link">コンタクト</a></li>
</ul>

CSS:

/* 親は float を解除する */
.menu-float {
  overflow: hidden;
}
/* 子要素は display と float 、 width を指定する */
.menu-float .menu-item {
  display: block;
  float: left;
  width: 25%;
  min-width: 100px;

  text-align: center;
}
.menu-float .menu-link {
  /* 英語と日本語が混じったときにも高さを共通にするため line-height を指定する */
  line-height: 24px;
  /* ユーザビリティを考慮しクリッカブルエリアを十分に取る */
  display: block;
  padding: 8px 10px;
  word-wrap: break-word;
}

2. inline-block を使う方法

HTML:

<ul class="menu menu-inline">
  <li class="menu-item"><a href="" class="menu-link">ホーム</a></li>
  <li class="menu-item menu-item-active"><a href="" class="menu-link">アバウト</a></li>
  <li class="menu-item"><a href="" class="menu-link">serviceserviceserviceserviceservice</a></li>
  <li class="menu-item"><a href="" class="menu-link">コンタクト</a></li>
</ul>

CSS:

/* 親に必須のスタイルはなし */
.menu-inline {
  /* メニュー全体を中央寄せにするには親要素に text-align center を指定する */
  text-align: center;
}
/* 子要素は display: inline-block と vertical-align を指定する */
.menu-inline .menu-item {
  display: inline-block;
  vertical-align: middle;
  /* 子要素間の間隔を指定する */
  margin: 0 10px;
}
.menu-inline .menu-link {
  display: block;
  max-width: 200px;
  /* ユーザビリティを考慮しクリッカブルエリアを十分に取る */
  padding: 8px 10px;
  /* 万が一長過ぎるアイテムが発生したときのために max-width を指定しておくと適宜改行が発生する */
  word-wrap: break-word;
}

3. table レイアウトを使う方法

HTML:

<ul class="menu menu-table">
  <li class="menu-item"><a href="" class="menu-link">ホーム</a></li>
  <li class="menu-item menu-item-active"><a href="" class="menu-link">アバウト</a></li>
  <li class="menu-item"><a href="" class="menu-link">serviceserviceserviceserviceservice</a></li>
  <li class="menu-item"><a href="" class="menu-link">コンタクト</a></li>
</ul>

CSS:

/* 親は display: table 、 table-layout: fixed にして width を指定する */
.menu-table {
  display: table;
  table-layout: fixed;
  width: 100%;
}
/* 子要素は display: table-cell 、 vertical-align 、 text-align を指定する */
.menu-table .menu-item {
  display: table-cell;
  vertical-align: middle;
  text-align: center;
}
.menu-table .menu-link {
  display: block;
  padding: 8px 10px;
  word-wrap: break-word;
}

4. flexbox を使う方法

こちらは割愛します( flexbox がもっと一般化してきたら追加します)。

以上のコードを gist に置いていますのでこちらもよろしければ。

CSS は定番パターンがある程度決まっているので、 mixin などを使って素早く快適に作っていきたいものですね。

以上です。