Nuxt.jsに組み込んだBulmaでスティッキーフッター (sticky footer) を実現する


はじめに

前回書いた記事「 Nuxt.jsにBulmaを組み込んだら、Vuexストアが理解できた件」の続きみたいなものです。少なくとも設定は同じです。

Bulmaには Footer が、ちゃんと1ページ使って説明されているのに、下部に固定化する方法についてはなにも言及していません。どうやって実現するのかなと調べたら、レイアウトファイルひとつで簡単に実現できたという、またまたアハ体験が待っておりました。

駆け足で、前回の記事を再現

  • create-nuxt-appコマンドを使ってavocadoという名前のNuxt.jsアプリを作成
  • pagesディレクトリにabout.vuecontact.vueの2つのファイルを作成し、インデックスページ以外のページを用意
  • componentsディレクトリにNavbar.vueを作成し、ナビゲーションメニューのコンポーネントを用意
  • layouts/default.vueファイルを編集し、ナビゲーションメニューのコンポーネントをレイアウトファイルに取り付け
  • npm run devコマンドを叩いて開発サーバーを起動

avocadoのディレクトリの中はこんな感じになっています

|--components/
|    |--Logo.vue ... index.vueが使用するコンポーネント
|    `--Navbar.vue ... ナビゲーションメニュー
|--layouts/
|    `--default.vue ... デフォルトのレイアウトファイル
`--pages/
     |--about.vue ... 新たに作成したページ
     |--contact.vue ... 新たに作成したページ
     `--index.vue ... 最初から用意されているトップページ

前回の記事ではページ遷移後のメニューのトグルにVuexストアを使っていましたが、@takanoripさんの貴重なコメントにより、Navbar.vueファイルだけで実現できるようになりました。store/index.jsファイルは登場しません。

フッターコンポーネントの作成

componentsディレクトリにBottom.vueファイルを作成します。

components/Bottom.vue
<template>
  <footer class="footer">
    <div class="container">
      <div class="content has-text-centered">
        <p>
          &copy; 2018 Avocado
        </p>
      </div>
    </div>
  </footer>
</template>

今回は、新しいレイアウトファイルを用意します。
layoutsディレクトリにstickyfooter.vueファイルを作成します。
そこにフッターコンポーネントを取り付けます

layouts/stickyfooter.vue
<template>
  <div>
    <navbar/>
    <nuxt/>
    <bottom/>
  </div>
</template>

<script>
import Navbar from '~/components/Navbar.vue'
import Bottom from '~/components/Bottom.vue'

export default {
  components: {
    Navbar,
    Bottom
  }
}
</script>

なぜFooter.vueではなく、Bottom.vueというファイル名にしたかというと、コンポーネントを取り付けるときに、<footer/>という要素名が、すでにHTML5で採用された要素とダブるからです。

話の流れとは直接関係ない修正

pages/index.vueファイルのスタイル定義が全ページに影響を及ぼしていて、こちらが意図したレイアウトの邪魔をするので、ローカルスコープにします。つまり「そのスタイル定義はそのページのみで有効」にさせます。

pages/index.vue
<!-- <style>を<style scoped>に変更 -->
<style scoped>
.container
{
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;
}
...
</style>

すでにあるページに新しいレイアウトを適用

ページは何も指定しなければ、自動的にデフォルトのレイアウトが適用されますが、以下のように指定すればそのレイアウトファイルが適用されます。

pages/contact.vue
<template>
  <div>
    <section class="hero is-info is-bold">
      <div class="hero-body">
        <h1 class="title is-size-2">
          Contact
        </h1>
        <h2 class="subtitle is-size-4">
          お問い合わせ
        </h2>
      </div>
    </section>
    <div class="container">
      お問い合わせ
    </div>
  </div>
</template>

<script>
export default {
  layout: 'stickyfooter'
}
</script>

その結果がこちらになります。

はい、フッターが浮いております。フッターの下に何もない(恥ずかしい)空間がある。
Bulmaのフッターは特に極太仕様になっておりますが、それでも、スマホの縦長画面では浮いてしまいがちです。このフッターをどうやって下げるかが、本日のお題です。

やっと辿り着いた感が半端ないですが、間もなく終わります。

スティッキーフッター (sticky footer)の実現

layouts/stickyfooter.vueファイルを修正します。
sf-site-allsf-site-contentという2つのCSSクラスを追加します

layouts/stickyfooter.vue
<template>
  <div class="sf-site-all">
    <navbar/>
    <nuxt class="sf-site-content"/>
    <bottom/>
  </div>
</template>

<script>
import Navbar from '~/components/Navbar.vue'
import Bottom from '~/components/Bottom.vue'

export default {
  components: {
    Navbar,
    Bottom
  }
}
</script>

<style>
.sf-site-all {
  min-height: 100vh; /* 全体の高さを最低でもビューポートの100%にする */
  display: flex; /* 子クラスを横並びにする */
  flex-direction: column; /* 子クラスの横並びの方向を縦にする */
}
.sf-site-content {
  flex: 1; /* flexに1つだけ数値を指定するとその要素は伸びる */
}
/* NavbarとBottomにはflexを指定しないので伸びない */
/* 結果としてページファイルの中味だけが伸びて、ビューポート100%を実現 */
/* Bottomは下に貼り付く。スティッキーフッターを実現 */
</style>

その結果がこちらです。

私のアハ体験ふたつ

「flexboxを使うとこんなに簡単にスティッキーフッターが実現できてしまうのか!」というのがひとつ。今まで後生大事にとっておいたスティッキーフッターのためのCSSテンプレートは捨ててしまいました。

「レイアウトファイルとページファイルを組み合せるための目印になっている<nuxt/>にもクラス名を付加してよかったんだ!」というのがもうひとつ。どこにも書いてなかったので、できるとは思わなかったのですが、あとから考えてみたら、「どうしてできないと思ったのか?」のほうが不思議ですね。

最後に

冒頭にも書いた通り、Bulmaのフッターを下部に固定する方法はどこにも載っていないのですが、Bulmaの作者からすると「なんで、そんなことわざわざ書かなきゃいけないの?」って感じなんだと思います。「Flexboxを採用したBulmaを採用したあなたなら知ってて当然でしょう」という感覚。いずれ私もそっち側の人間になりたいなと思いつつ、その過程で生まれたアハ体験を、次の人のために記事にしておく次第です。

面白いと思っていただけたら「いいね」をクリックしてください。励みになります。