Publishでカスタムテーマを作成する方法(Swift)

「PublishでWebサイトを構築する」は6部構成です。
記事を順番に読み進めると、PublishでWebサイトが構築できるようになります。

第1部: [セットアップ&使い方](https://qiita.com/uhooi/items/57e040df2f4aa7f62d89)
第2部: カスタムテーマの作成　__←イマココ__
第3部: [プラグインのインストール](https://qiita.com/uhooi/items/bf0ff67d687b53a11c5b)
第4部: プラグインの作成（未投稿）
第5部: [ツイートボタンの表示](https://qiita.com/uhooi/items/9aa046f0a42e5afbd7c3)
第6部: [はてなブックマークボタンの表示](https://qiita.com/uhooi/items/40cb71ac3aca57853544)

## はじめに

Publishでテーマをカスタマイズする方法を紹介します。

## 環境

- OS：macOS Monterey 12.2.1
- Xcode：13.3 (13E113)
- Swift：5.6
- Publish：0.8.0

## 既存テーマの複製

0からテーマを作成するのは手間なので、本記事では `foundation` テーマを複製して作成します。
複製時はライセンスに遵守するよう気をつけてください。

以下から `foundation` テーマのSwiftファイルとCSSをダウンロードします。

- [Theme+Foundation.swift](https://github.com/JohnSundell/Publish/blob/9e1b20a23c73ba078966a4fcce23146088d0de63/Sources/Publish/API/Theme+Foundation.swift)
- [styles.css](https://github.com/JohnSundell/Publish/blob/9e1b20a23c73ba078966a4fcce23146088d0de63/Resources/FoundationTheme/styles.css)

Swiftファイルは `Sources/{パッケージ名}` フォルダ以下に格納します。
CSSは `Resources` フォルダ以下に格納します。

私は `uhooi` というテーマを作成するので、ファイル名とフォルダ構成は以下としました。

```shell-session
$ tree
.
├── Resources
│   └── UhooiTheme
│       └── styles.css
└── Sources
    └── WebsiteSample
        ├── Theme+Uhooi.swift
        └── main.swift
```

テーマ名やパスなどを修正します。

```diff_swift:Theme+Uhooi.swift
+ import Publish
import Plot

public extension Theme {
-   static var foundation: Self {
+   static var uhooi: Self {
        Theme(
-           htmlFactory: FoundationHTMLFactory(),
-           resourcePaths: ["Resources/FoundationTheme/styles.css"]
+           htmlFactory: UhooiHTMLFactory(),
+           resourcePaths: ["Resources/UhooiTheme/styles.css"]
        )
    }
}

- private struct FoundationHTMLFactory<Site: Website>: HTMLFactory {
+ private struct UhooiHTMLFactory<Site: Website>: HTMLFactory {
    func makeIndexHTML(for index: Index,
                       context: PublishingContext<Site>) throws -> HTML {
        // ...
    }
    // ...
}
```

作成したテーマを使うように修正します。

```diff_swift:main.swift
try WebsiteSample().publish(using: [
    .addMarkdownFiles(),
    .copyResources(),
-   .generateHTML(withTheme: .foundation),
+   .generateHTML(withTheme: .uhooi),
    .generateRSSFeed(including: [.posts]),
    .generateSiteMap(),
    .deploy(using: .gitHub("uhooi/uhooi.github.io", useSSH: false))
])
```

`publish-cli run` を実行して問題ないことを確認したら、既存テーマの複製が完了です。

## テーマのカスタマイズ

テーマを複製したら、カスタマイズしていきます。

### HTMLのカスタマイズ

HTMLは [Plot](https://github.com/JohnSundell/Plot) というPublishと同じ作者が提供しているライブラリを使って書きます。
SwiftUIのようなDSLで、HTMLやRSSなどをSwiftを使って型安全に書けます。

試しに簡単な修正をしてみます。
フッターの「RSS feed」を「RSSフィード」に変更します。

```diff_swift:Theme+Uhooi.swift
private struct SiteFooter: Component {
    var body: Component {
        Footer {
            Paragraph {
                Text("Generated using ")
                Link("Publish", url: "https://github.com/johnsundell/publish")
            }
            Paragraph {
-               Link("RSS feed", url: "/feed.rss")
+               Link("RSSフィード", url: "/feed.rss")
            }
        }
    }
}
```

`publish-cli run` を実行してWebサイトを確認します。

|Before|After|
|:--:|:--:|
|![スクリーンショット 2022-03-29 10.59.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/46cc3d42-21ee-28a0-a6b9-cd0fd2309778.png)|![スクリーンショット 2022-03-31 15.25.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/899b9c90-58c5-0067-a03c-e18bc0ce4efc.png)|

ページ下部の文言が変わったことがわかります。

このようにしてHTMLをカスタマイズできます。

`Publish.Website` に準拠した構造体は `context.site` でアクセスでき、 `url` や `name` 、 `description` などを取得できます。

```swift:main.swift
struct WebsiteSample: Publish.Website {
    // ...
    var url = URL(string: "https://uhooi.github.io")!
    var name = "WebsiteSample"
    var description = "A description of WebsiteSample"
    // ...
}
```

```swift:Theme+Uhooi.swift
private struct UhooiHTMLFactory<Site: Website>: HTMLFactory {
    // ...
    func makeItemHTML(for item: Item<Site>, context: PublishingContext<Site>) throws -> HTML {
        print(context.site.url.absoluteString) // "https://uhooi.github.io"
        print(context.site.name) // "WebsiteSample"
        print(context.site.description) // "A description of WebsiteSample"
        // ...
    }
    // ...
}
```

これを覚えておくと、Webサイトのカスタマイズの幅が広がります。
他にも `context` でいろいろな情報へアクセスできるので、詳細は定義を追ってみてください。

### CSSのカスタマイズ

CSSのカスタマイズは、そのままCSSを更新するだけです。

試しに背景色を白から青にしてみます。

```diff_css:styles.css
body {
-   background: #fff;
+   background: blue;
    color: #000;
    font-family: Helvetica, Arial;
    text-align: center;
}
```

`publish-cli run` を実行してWebサイトを確認します。
ChromeではCSSをキャッシュすることがあるので、更新されない場合はスーパーリロード（Command + Shift + R）すると反映されます。

|Before|After|
|:--:|:--:|
|![スクリーンショット 2022-03-29 10.59.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/ad29df86-4345-9b3d-044d-a198a9bb6cbd.png)|![スクリーンショット 2022-03-31 15.38.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/138245/dee4e5cc-f92a-3001-b558-796c4c4399ba.png)|

背景色が変わったことがわかります。

このようにしてCSSをカスタマイズできます。

デフォルトだとURLが折り返されないので、修正すると読みやすくなります。

```diff_css:styles.css
.content {
    margin-bottom: 40px;
+   overflow-wrap: break-word;
}
```

こちらの修正は [PR](https://github.com/ios-osushi/website/pull/16) を頂いて対応しました。
[本家にも同様のPR](https://github.com/JohnSundell/Publish/pull/130) を作成しています。

## おわりに

これでPublishでカスタムテーマを作成できました！
オリジナルのテーマを作成して自分だけのWebサイトを作りましょう :relaxed:

## 参考リンク

- [JohnSundell/Publish: A static site generator for Swift developers](https://github.com/JohnSundell/Publish#building-an-html-theme)
- [Creating a Custom Theme Using Publish | Russell Gordon](https://www.russellgordon.ca/tutorials/creating-a-custom-theme-using-publish/)