🔖

Gatsbyブログに目次を実装する

このブログに目次(toc)を実装したいと思います。

目次の実装方法は2パターンありそうです。

  1. ライブラリを使用してMarkdown内で目次を表示するタグを追加する方法
  2. GraphQL から取得した目次情報を自作したUIコンポーネントで表示する方法

本当は後者で実装したいところですが、レイアウトの変更に時間が掛かりそうなので、 前者のライブラリを使う方向で実装してみたいと思います。

やること

ざっとやることを調べてみたところ、👇 のような設定・実装を行う必要があるようです。 とりあえず進めてみます。

  • GraphQL から目次(Table of Contents)を取得できるようにする
  • TOCライブラリを追加
  • 目次のリンクから見出しへジャンプできるようにする

GraphQL から目次(Table of Contents)を取得できるようにする

gatsby-transformer-remarkがインストールされていれば、GraphQLから目次情報を取得できます。
僕の環境ではインストール済みなのでここはスキップします。

TOCライブラリの追加と設定

ライブラリはこちらを使用してみたいと思います。
追加コマンドはこちら。

$ yarn add gatsby-remark-table-of-contents

見出しをリンクにするライブラリも追加する必要があります。
これを追加しないと目次クリックしても見出しにジャンプしません。

$ yarn add gatsby-remark-autolink-headers

続いてgatsby-config.jsの設定を行います。内容はプラグイン説明からコピペしました。

module.exports = ({ root }) => ({
  plugins: [
    {
      resolve: `gatsby-transformer-remark`,
      options: {
        plugins: [
          {
            resolve: `gatsby-remark-table-of-contents`,
            options: {
              tight: false,
              ordered: false,
              fromHeading: 1,
              toHeading: 6,
              className: "table-of-contents",
            },
          },
          `gatsby-remark-autolink-headers`,
        ],
      },
    },
  ],
});

デザインの調整

ライブラリを追加するだけでは見た目が寂しい感じだったので、デザインを調整します。

まずはtocタグを追加した時に挿入されるHTMLを確認します。 classを紐付けられそうな目印がdiv.table-of-contentsぐらいしかないので、とりあえずここを基準にスタイルを設定します。

<div class="table-of-contents">
  <ul>
    <li><a href="xxx">やること</a></li>
    <li><a href="xxx">GraphQL から目次(Table of Contents)を取得できるようにする</a></li>
    <li>
      <p><a href="xxx">TOC ライブラリを追加</a></p>
      <ul>
        <li><a href="xxx">ライブラリのインストール</a></li>
        <li><a href="xxx">目次のリンクから見出しへジャンプできるようにする</a></li>
        <li><a href="xxx">gatsby-config.js の設定</a></li>
      </ul>
    </li>
    <li><a href="xxx">デザインの調整</a></li>
  </ul>
</div>

いろいろ試行錯誤した結果、書いたスタイルはこちら。
コンポーネントにして使うことにしました。

import { css } from "styled-components";

const postTableOfContents = css`
  .table-of-contents {
    display: flex;
    flex-direction: column;
    margin-bottom: 2rem;
    max-height: calc(100vh - 1.5rem - 340px);
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 2px 4px rgb(0 0 0 / 40%);
    padding: 1.5rem;
  }

  .table-of-contents ul {
    flex: 1 1 auto;
    font-size: 0.9rem;
    line-height: 2;
    margin: 0;
  }

  .table-of-contents ul li {
    list-style-type: none;
  }

  .table-of-contents ul li::before {
    color: #ccc;
    content: "#";
    display: inline-block;
    margin-right: 0.5rem;
  }

  .table-of-contents a:hover {
    color: #20a8ea;
  }

  .table-of-contents a {
    color: inherit;
    text-decoration: none;
  }
`;

export default postTableOfContents;

忘れっぽい自分のためのノートです。調べたことや試したことをストックしていきます。