はじめに
静的サイトジェネレーターのHUGO、Node.js製のCSSフレームワークであるPostCSS、ビルド + ホスティングサービスのNetlifyを使ってポートフォリオを作ったので、作り方を公開したいなと思い、作成しました。
https://shinya-sato.com/
今回は 「どうやって作ったか」 について書いているので、ポートフォリオの構成とかについては原則書かない事にしておきます。
HUGO
HUGOとは、Go言語製の静的サイトジェネレーターです。
「シンプルだが多機能」という特徴があります。
生のhtmlをそのまま編集するのと同じ要領で、「関連記事の表示」「多言語対応」等の高等な機能も持ち合わせています。
何故HUGOを選んだのか
結論からいうと、 参考にしたポートフォリオサイトがHUGOで実装されていたからです。
Jekyllはbuildが遅いし、カスタム投稿のページネーションは実装できないなど、痒いところに手が届かなかったので、他の静的サイトジェネレーターを探していました。
StaticGenを見たら、GithubのStar数も多いので使ってみました。
HUGOをインストールする
まずはhomebrewを使って、HUGOをインストールします。
brew install hugo
Repositoryをcloneする
さっそく作り始める、のではなく、 参考にするRepositoryをcloneするところから 始めます。
そして、 技術を徹底的にパクリまくります。
HUGOでサイトを作り始める
ある程度技術を盗んだら、早速HUGOでサイトを作り始めます。
hugo new site directory_name
Congratulations! Your new Hugo site is created in /Users/cookboys/documents/directory_name.
Just a few more steps and you're ready to go:
1. Download a theme into the same-named folder.
Choose a theme from https://themes.gohugo.io/, or
create your own with the "hugo new theme <THEMENAME>" command.
2. Perhaps you want to add some content. You can add single files
with "hugo new <SECTIONNAME>/<FILENAME>.<FORMAT>".
3. Start the built-in live server via "hugo server".
Visit https://gohugo.io/ for quickstart guide and full documentation.
HUGOを立ち上げてみる
コマンドを叩いたら、HUGOのサイトが出来ていると思いますので、ディレクトりに移動します。
cd directory_name
ディレクトりに移動したら、コマンドを叩いて、HUGOを立ち上げてみたいと思います。
hugo server -w
何も表示されていません(Jekyllと違って、サンプル画面すらありません)
HUGOは必要最小限の動作環境だけ提供されるので、ここから自分で環境を構築していきます。
PostCSSを導入する
必要最小限の環境しかありませんので、AltCSSの環境も自分で用意する必要があります。
なので今回は、Node.js製のCSSフレームワークである、PostCSSを導入してみたいと思います。
なぜPostCSSなのか
単刀直入に言うと、
- Sassに飽きたから
- トランスパイルが早いから
- 実績ある企業が採用しているから(Facebook, Github, Qiita, Taobao等)
そもそもPostCSSって何?
PostCSSとは、先ほど申した通り、Node.js製のCSSフレームワークです。
Node.js製といえば、StylusというCSSプリプロセッサーがありますが、PostCSSはStylusと違い、インストールしただけでは何もしれくれません!
「CSSフレームワーク」の名の通り、PostCSSはプラグインを追加して、自分独自のCSSプリプロセッサーを作るツールなのです。
なので、プラグインを追加しないと、何もしれくれません。
PostCSSの場合
Node.jsが、ファイルをCSSに変換するのはStylusと同じですが、拡張子が.cssのままです。
当然ですが、このままでは使えるCSSになりません。
@reset-global pc;
も、@custom-media --smartphone(width <= 600px);
も、CSSの構文には存在しないからです。
これらを使えるようにするには、PostCSSのプラグインを追加しないといけません。
PostCSSを使えるようにする
では実際に、PostCSSを使えるようにしていきたいと思います。
npmを導入する
HUGOのディレクトリで、以下のコマンドを実行して、npmを導入します。
npm init
yarnをインストールする
以下のコマンドを実行して、yarnをインストールします。
npm install -g yarn
PostCSSをインストールする
以下のコマンドを実行して、PostCSSをインストールします。
yarn add postcss
CLIをインストールする
以下のコマンドを実行して、postcss-cliをインストールします。
yarn add postcss-cli
CLIの設定をする
CLIを使うために、設定ファイルの作成と、スクリプトを設定します。
- postcss.config.js
module.exports = (ctx) => ({
plugins: []
});
- package.json
{
"scripts": {
"start": "hugo server -w & postcss postcss/*.css -d static/assets/styles/ -w",
"build": "hugo & postcss postcss/*.css -d docs/assets/styles/"
},
"dependencies": {
"postcss": "^6.0.19",
"postcss-cli": "^5.0.0"
}
}
HUGOを実行するスクリプトと、PostCSSを実行するスクリプトを一緒にしました。
これで、yarn start
を実行すると、HUGOとPostCSSが同時に起動します。
PostCSSのプラグインをインストールする
さて、準備ができましたので、この画像のCSSを使えるようにするために、PostCSSのプラグインをインストールします。
yarn add postcss-css-reset postcss-mixins postcss-simple-vars postcss-color-function postcss-nested postcss-custom-media postcss-media-minmax
-
postcss-css-reset
- PostCSSでreset.cssを設定できる。
-
postcss-mixins
- Postcssでmixinsを設定できる。
-
postcss-simple-vars
-
$
を使った変数を使うことができる。
-
-
postcss-color-function
- color変数を使うことができる。
-
postcss-nested
- PostCSSでNest(入れ子)を使うことができる。
-
postcss-custom-media
-
@media
に名前をつけてコンポーネント化することができる。
-
-
postcss-media-minmax
-
@media
を不等号で指定できる。
-
configファイルに設定を記載する
プラグインを追加しただけではまだダメなので、postcss.config.js
に設定を記載します。
module.exports = (ctx) => ({
plugins: [
require('postcss-css-reset'),
require('postcss-mixins'),
require('postcss-simple-vars'),
require('postcss-color-function'),
require('postcss-nested'),
require('postcss-custom-media'),
require('postcss-media-minmax')
]
});
この順番通りにプラグインが読み込まれるので、順番は非常に重要です。
HUGOで条件分岐をする
PostCSSの設定が終わったので、HUGOに戻ります。
トップページのタイトルを変える
トップページはタイトルを変えたいので、条件分岐を設定していきます。
<title>{{if .IsHome}}{{ .Site.Params.homeTitle }}{{ else }}{{ .Title }}{{ end }} | {{ .Site.Params.SiteName }}</title>
これで、トップページの際はconfig.tomlで設定した、homeTitle
が呼び出されて、それ以外のページはHUGOのFrontMatterで設定したTitle
が呼び出されます。
その他の条件分岐も設定していきます
<meta name="keywords" content="{{ with .Params.Keyword }}{{ . }}{{ else }}{{ .Site.Params.Keyword }}{{ end }}">
<meta name="description" content="{{ with .Params.Description }}{{ . }}{{ else }}{{ .Site.Params.Description }}{{ end }}">
<meta property="og:title" content="{{if .IsHome}}{{ .Site.Params.homeTitle }}{{ else }}{{ .Title }}{{ end }} | {{ .Site.Params.SiteName }}">
<meta property="og:description" content="{{ with .Params.excerpt }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<meta property="og:url" content="{{ .Permalink }}">
<meta property="og:image" content="{{ .Site.BaseURL }}{{ with .Params.ogp }}{{ . }}{{ else }}images/ogp.jpg{{ end }}">
<meta property="og:type" content="{{if .IsHome}}website{{ else }}article{{ end }}">
<meta property="og:site_name" content="{{ .Site.Params.SiteName }}">
<meta property="og:locale" content="{{ .Site.Params.LanguageCode }}">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{if .IsHome}}{{ .Site.Params.homeTitle }}{{ else }}{{ .Title }}{{ end }} | {{ .Site.Params.SiteName }}">
<meta name="twitter:description" content="{{ with .Params.excerpt }}{{ . }}{{ else }}{{ .Site.Params.description }}{{ end }}">
<meta name="twitter:image:src" content="{{ .Site.BaseURL }}{{ with .Params.ogp }}{{ . }}{{ else }}images/twitter_ogp.jpg{{ end }}">
<meta name="twitter:domain" content="{{ .Site.Params.Domain }}">
<meta name="keywords" content="{{ with .Params.Keyword }}{{ . }}{{ else }}{{ .Site.Params.Keyword }}{{ end }}">
上記の条件分岐の場合、HUGOのFrontMatter内にkeyword
があれば呼び出し、ない場合はconfig.tomlで設定した、Keyword
が呼び出されます。
---
title: "title"
keyword: "keywordが入ります"
date: 2018-04-05
categories: ['photos']
caption: "Photoshop"
eyecatch: '/images/photos/photo01_firstview_ja.png'
ogp: 'images/photos/photo01_firstview.png'
url: '/photos/photo01/'
draft: false
---
上記の場合は、keywordが入ります
が呼び出され、もしない場合はconfig.tomlで設定した、Keyword
が呼び出されます。
部品をコンポーネント化していく
HUGOのPartialを使って、繰り返し使いそうな部品はコンポーネント化しておきます。
<!DOCTYPE html>
<html lang="{{ .Site.Params.LanguageCode }}">
<head>
<meta charset="UTF-8">
<title>{{if .IsHome}}{{ .Site.Params.homeTitle }}{{ else }}{{ .Title }}{{ end }} | {{ .Site.Params.SiteName }}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
<meta name="keywords" content="{{ with .Params.Keyword }}{{ . }}{{ else }}{{ .Site.Params.Keyword }}{{ end }}">
<meta name="description" content="{{ with .Params.Description }}{{ . }}{{ else }}{{ .Site.Params.Description }}{{ end }}">
{{ partial "ogp.html" . }}
{{ partial "styles.html" . }}
</head>
多言語対応していく
作っていく途中で多言語対応をしたくなったので、やってみました。
これが、HUGOの目玉機能の一つです。
参考にしたポートフォリオは多言語対応していなかったので、Githubを検索しまくって、参考になりそうなRepositoryを探します。
SkycoinのRepositoryを真似る
Skycoinのブログが、一番機能的に豊富で、わかりやすい構成になっていたので、
SkycoinのブログのRepositoryを真似ることにしました。
Netlifyに対応させていく
最初はGithub Pagesを使ってサイトを公開していましたが、以下の理由から、Netlifyに乗り換えました。
- 独自ドメインのSSL対応が大変
- 毎回buildコマンドを打つのが面倒になった
- Formを使ってみたくなった
Formは、Netlifymが提供しているNetlify Formsを使って、簡単に導入することができました。
<div class="form__wrapper">
<form name="contact" netlify-honeypot="bot-field" action="thank-you" method="post" netlify="true">
<input type="hidden" name="form-name" value="contact" />
<h2>{{ i18n "formTitle" }}</h2>
<div class="field__container">
<div class="field__contents">
<div class="mc-field-group field__input">
<div class="form__label__wrapper">
<figure class="gopher__wrapper"><img src="{{ .Site.BaseURL }}images/icon/gopher_h.svg" alt=""></figure>
<label class="form__label">{{ i18n "formName" }}<span class="span__required">{{ i18n "formRequired" }}</span></label>
</div>
<input type="text" name="NAME" required>
</div>
<div class="mc-field-group field__input">
<div class="form__label__wrapper">
<figure class="gopher__wrapper"><img src="{{ .Site.BaseURL }}images/icon/gopher_u.svg" alt=""></figure>
<label class="form__label">{{ i18n "formMailaddress" }}<span class="span__required">{{ i18n "formRequired" }}</span></label>
</div>
<input type="email" name="EMAIL" required>
</div>
</div>
<div class="field__contents">
<div class="mc-field-group field__textarea">
<div class="form__label__wrapper">
<figure class="gopher__wrapper"><img src="{{ .Site.BaseURL }}images/icon/gopher_o.svg" alt=""></figure>
<figure class="gopher__wrapper"><img src="{{ .Site.BaseURL }}images/icon/gopher_g.svg" alt=""></figure>
<label class="form__label">{{ i18n "formInquiry" }}<span class="span__required">{{ i18n "formRequired" }}</span></label>
</div>
<textarea name="Message" cols="30" rows="10" required></textarea>
</div>
</div>
</div>
<div class="form__button__wrapper">
<button class="submit__button" type="submit">{{ i18n "formSubmit" }}</button>
</div>
</form>
</div>
まとめ
HUGOはシンプルだが多機能、かつ動作も速く、
PostCSSは設定が大変だが、これもNode製だから動作も早く、プラグインを組み合わせて自分だけのCSSプリプロセッサーを作ることができます。
Netlifyは、ただのホスティングサービスだけでなく、静的サイトジェネレーターの弱点であるFormまで補ってくれる、とても素晴らしいサービスでした。