Gatsby Themeの超入門

GatsbyのTheme周辺を自分の言葉でまとめた記事です。触れている内容はチュートリアルやドキュメントにあるものなので、それらすべてに目を通しているような人はここでブラウザを閉じてOKです。

公式チュートリアルを流し読みした後のおさらいとでもいいましょうか。

Gatsby Themeとは

コンポーネント, レイアウト, GraphQLなど、Gatsbyを構成する要素をnpmパッケージ化したものです。よく比較されるのがスターターです。スターターはそれ1つが構成単位のため、導入後は基底のスターターから乖離してしまいます。例えば、バージョンアップを受けるのが難しくなります。テーマは構成を別パッケージとして分けているため、それが容易になります。

テーマはプラグインのようなもので、複数組み込むことが可能です。重複する部分は構成設定の順序で優先度が決まります。具体的にはgatsby-config.jsplugin配列で最後に定義されたものが優先されていきます。

公式テーマのインストール方法

公式に沿い、ブログを構成するgatsby-theme-blogというテーマ見てみます。

NPMページにアクセスすると、インストール方法が2種類表示されています。1つはスターターを使ったもの、もう1つはマニュアルインストールで、既存のプロジェクトに追加したい場合に使います。


# [1] starter
gatsby new my-themed-blog https://github.com/gatsbyjs/gatsby-starter-blog-theme

# [2] manual
yarn add gatsby-theme-blog

このように公式のテーマは、テーマを即利用できるようにしたスターターが用意されています。「えっ、結局スターターを使うの?」と思うかもしれません。

テーマ込みのスターターも通常のスターター同様に、プロジェクト開始に最低限必要なファイルなどを作成してくれます。これは各テーマでディレクトリ構造の要件が異なるため有益です。

gatsby-theme-blogにしても、投稿記事のマークダウンファイルはcontent/postsに置くなど、通常のGatsbyのページの構成ルール(src/pages)とは異なります。そのような詳細は各テーマのREADME.mdに記載されています。テーマは複雑な構成やレイアウト構築のステップを舞台裏で行なってくれますが、ある程度のセットアップは自分で行う必要があります。スターターが用意されている場合は、その雛形をコマンド1つで作りだしてくれるというわけです。

ちなみに、gatsby-starter-themegatsby-theme-bloggatsby-theme-notesの2つのテーマが導入されています。

マニュアルインストールを試す

公式チュートリアルはスターターを使っているため、マニュアルインストールを試してみます。


# ミニマム構成でプロジェクト開始
mkdir sample && cd $_
yarn init -y
yarn add react react-dom gatsby
touch gatsby-config.js

# テーマインストール
yarn add gatsby-theme-blog

gatsby-config.jspluginsにテーマを指定すれば有効になります。

module.exports = {
  plugins: [
    {
      resolve: `gatsby-theme-blog`,
      options: {
        contentPath: `content/posts`,
        basePath: `/`,
        assetPath: `content/assets`,
        mdx: true
      }
    }
  ]
};

通常、Gatsbyはsrc/pagesにあるファイルをページにしますが、このテーマはデフォルトでcontent/posts内のマークダウンファイルをページとして生成します。また、トップページも独自の投稿一覧が生成されるため、src/pages/index.jsすら不要です。これらがテーマを1つ導入するだけで実現できます。

上記のオプションはデフォルト値と同じため今回は記述しなくても問題ありませんが、どういったオプションが用意されているかを知るのは大事です。上記の詳細は以下です。また、オプションの内部挙動については、このあと少し触れます。

  • contentPath – マークダウンファイルの位置
  • basePath – ベースとなるURLパス
  • assetPath – 画像などのデータの位置
  • msx – mdxの構成をgatsby-theme-blogに任せるか

それでは、投稿記事を1つ配置してみましょう。

mkdir -p content/posts && $_
touch hello-world.md

マークダウンファイルには、以下を記述します。

---
title: "Hello, World"
date: 2019-11-26
---

Hello World!

gatsby developを実行し、http://localohst:8000にアクセスするとブログが生成できているのが確認できます。

通常、ローカルファイルをHTMLに変換して特定URLで表示するには、一連の手順・ボイラープレートがあります。各種プラグインをを導入し、GraphQLでnodeオブジェクトを問い合わせ、テンプレートを指定してページを生成する…これらの煩雑な作業を1つのテーマを入れるだけで解決することができました。

オプションの内部挙動

先のオプションに定義した値は、インストールしたテーマ(node_module/gatsby-theme-blog)内のファイルで利用されます。ディレクトリ構造を見えばわかるように、一般的なGatsbyプロジェクトとほぼ同じです。

gatsby-theme-blogのディレクトリ構造

このため、構成面の設定はgatsby-config.jsに記載されているというのが予想できます。

以下のようなコードが確認できます。

// node_modules/gatsby-theme-blog/gatsby-config.js

module.exports = options => {
  return {
    plugins: [
      {
        resolve: `gatsby-theme-blog-core`,
        options,
      },
      `gatsby-plugin-react-helmet`,
      `gatsby-plugin-twitter`,
      `gatsby-plugin-emotion`,
      `gatsby-plugin-theme-ui`,
    ],
  }
}

テーマの中で更にgatsby-theme-blog-coreというテーマが利用されているのが確認できます。これがブログのコアテーマです。先に記述したcontentPathなどのオプションは、実際にはこちらで処理されています。


// node_modules/gatsby-theme-blog-core/gatsby-config.jsの抜粋

// themeOptionsにgatsby-theme-blogのoptionsで設定した値が格納されている
odule.exports = themeOptions => {
  const options = withDefaults(themeOptions)
	...  
  plugins: [
	...
  {
    resolve: `gatsby-source-filesystem`,
    // contentPathが利用されている
    options: {
      path: options.contentPath || `content/posts`,
      name: options.contentPath || `content/posts`,
    },
  },

contentPathが指定されていればそのパスをマークダウンファイルが配置されているパスとして利用し、指定がなければcontent/postsをデフォルト値をして利用するという具合です。

実際、ここまで細かく動作を追う必要はありませんが、内部挙動を知るのはシャドウイングを学ぶ前の良いプロセスです。

シャドウイング

テーマに含まれているコンポーネントやスタイルなどを変更・上書きする機能です。要はテーマの一部をカスタマイズしたいときの方法です。ここはドキュメントに詳しく書かれているので、要点だけ記述しておきます。

シャドウイングの手順です。

  1. srcディレクトリにテーマ名と同じディレクトリを作成する
  2. シャドウイングしたいファイルまでのディレクトリ構造を模範する(srcは省く)

例)

  • 元テーマ: gatsby-theme-blog/src/comopnents/bio-content.js
  • 自分のプロジェクト: src/gatsby-theme-blog/components/bio-content.js
// src/gatsby-theme/blog/components/bio-content.js

import React from "react";
export default () => <>New Bio</>;

これでインデックスページのプロフィール欄が「New Bio」という文字に置き換わります。テーマ内のsrc以下すべてのファイルがシャドウイングが可能です。

テーマ内のテーマのシャドウイング

先程、gatsby-theme-blogのディレクトリ内を見た時、gatsby-theme-blog-coreというディレクトリがあったのに気付いたでしょうか。gatsby-config.jsのプラグインに指定されていたブログのコアテーマと同じです。

これはテーマ内で他(コア)テーマのコンポーネントをシャドウイングしています。

// node_modules/gatsby-theme-blog-core/src/components/post.js
import React from "react"
export default props => <pre>{JSON.stringify(props.data, null, 2)}</pre>


// node_modules/gatsby-theme-blog/src/gatsby-theme-blog-core/components/post.js
import React from "react"
import Post from "../../components/post"

export default ({ location, data }) => {
  const { blogPost, previous, next } = data
  return (
    <Post
      data={{ ...data, post: blogPost }}
      location={location}
      previous={previous}
      next={next}
    />
  )
}

これは丸ごと上書きではなく、部分利用です。基底となるテーマに汎用性があると、派生テーマが作りやすくなります。

Theme UI

gatsby-theme-blogでも組み込まれているように、Gatsby ThemeのスタイリングはTheme UIが推奨されています。公式ドキュメントでレイアウトのシャドウイング例が取り上げられています。最後にこれについて少し触れておきます。

Theme UIとは

Theme UIは制約ベースのレイアウト構築ができるReactスタイルライブラリです。共有テーマオブジェクトに定義された"typography", "color", "layout"などの値を使用して任意コンポーネントのスタイルを設定できます。

Theme UIは、Emotion+Typography+Styled Stystemで構築されています。

Gatsbyとの関係性

Theme UIは、Gatsby Themeに必須ではありません。また、Gatsby専用のライブラリというわけではなく、プレーンなReactでも使用できます。

gatsby-theme-blogのようにTheme UIが組み込まれたテーマの場合はインストール不要ですが、未対応のプロジェクトの場合は以下のようにインストール可能です。

yarn add theme-ui gatsby-plugin-theme-ui @emotion/core @mdx-js/react

プラグインに追加すれば、有効になります。

module.exports = {
  plugins: ["gatsby-plugin-theme-ui"],
}

Gatsby Themeのシャドウイング

gatsby-plugin-theme-uiを使用するテーマがサイトにインストールされている場合、src/gatsby-plugin-theme-ui/index.jsをシャドウイングすると、カスタマイズのベースにしたり、規定のレイアウトを丸ごと上書きできます。このindex.jsが共通テーマオブジェクトです。

前述のようにgatsby-theme-blogは、Theme UIを使用しています。node_modules/gatsby-theme-blog/srcにはgatsby-plugin-theme-uiディレクトリがあり、デフォルトのスタイルが定義されているのが確認できます。

gatsby-plugin-theme-uiのディレクトリ

いくつかファイルを分けていますが、index.jsでインポートされています。プラグインを入れただけでブログの見栄えまで整えられているのは、Theme UIの共通テーマオブジェクトのデフォルトスタイルがgatsby-theme-blogに用意されているからです。

公式チュートリアルcolors.jsをシャドウイングしてブログ内の色を変更していますが、シャドウ元は上記画像にあるファイルです。シャドウイングして値を変更すれば、簡易な見栄えの変更は着せ替え感覚で行えます。

尚、ReactでTheme UIを使用するときは、ThemeProviderでアプリケーション全体をラップする必要がありますが、シャドウイングだと不要です。その他、Theme UIが提供する<Flex><Box>などの独自コンポーネントも使用可能です。

Theme UIの仕様はドキュメントを読むのがベストです。

おわりに

昨年ぐらいから耳にしていたGatsbyですが、自分は最近ようやく触り始めました。昨今、JAMstackという言葉も成熟してきており、SSG(Static Site Generator)であるGatsbyはプレゼンテーションレイヤーでよく利用されます。JAMstackという言葉自体は誇大宣伝のようにも感じているのですが、Gatsby自体は興味深く、学習価値はあるはずです。

特に今回触れたThemeは強力です。導入部分だけでしたが、可能性が伝われば幸いです。独自テーマの作成やTheme UIを使う意義など、掘り下げる要素は色々あるので機会があれば記事にしたいと思います。