Typstでブログを書くためのGitHubテンプレートを作成した
Typst良いですよね。Markdownのように手軽に書けるのに、図表、脚注、参考文献、数式まで楽に、しかも美しく扱えるところが特に気に入っています。
ZennでもTypstの記事が増えていて、この記事を書いている2026年5月26日時点では Typstトピックの記事数が100本 になっていました。おめでたいですね。
そんなTypstですが、バージョン0.13で実験的にHTMLエクスポートがサポートされ、0.14で組み込み要素の対応がかなり進みました。
公式ドキュメントでも執筆時点ではまだ開発中の実験的機能という扱いで、本番用途には使わないよう案内されています。実際、レイアウトや一部のHTMLまわりの挙動にはまだ未完成なところがあります。
とはいえTypst 0.14では多くの組み込み要素がHTMLとして出力されるようになっており、個人ブログ用途ならかなり使えるところまで来ていると感じました。
さらに手元で現在のTypstのmainブランチからビルドして試したところ、数式もMathMLを使ってWeb上で綺麗に表示されるようになっていました。$(v dot nabla) v < "nya"$ のようなインライン数式や、$ integral_0^1 x^2 dif x = 1 / 3 $ のようなブロック数式もかなり自然に表示されます。

現行リリースだけを見るとまだ粗い部分はありますが、HTMLエクスポートは今後も強化されていくだろうと期待しています。
こういったこともあり自分のブログでTypstを執筆に使っていました。最初はクラスレスCSSを当てて雑に使っていたのですが、記事を書いているうちに少しずつ不満が溜まっていったので、いっそ自分でTypst専用に作ってしまおうということで作ったのがこのテンプレートです。
テンプレート:https://github.com/minimarimo3/typst-blog-template
使える記法をまとめたサンプルページ:https://minimarimo3.github.io/typst-blog-template/example-post/
実際にこのテンプレートを使用しているブログ:https://www.minimarimo3.jp
これは何か
TypstのHTMLエクスポートを使ってブログを書くための静的サイトテンプレートです。
記事本文から、タイトル、作成日、更新日、タグ、概要などのメタデータも含めてTypstで記述可能です。サイト名、説明文、著者情報、テーマ、フォント、共有ボタンの有無といったサイト全体の設定もsite.typで管理します。
現在の構成ではユーザーが編集する部分とブログエンジン部分を分けています。
ユーザー側のリポジトリには、site.typ、記事ディレクトリ、static/、GitHub ActionsのWorkflowなどを置きます。一方で、テンプレートの共通実装はvendor/typst-blog-coreにGit submoduleとして取り込んでいます。
ルートのbuild.pyやtemplate.typは薄い入口になっていて、実際のビルド処理、Typstコンポーネント、CSS、JavaScript、テーマなどはcore側にあります。これにより、記事や自分のサイト設定を残したままブログエンジン部分だけをあとから更新しやすくしています。
あとは build.py を実行するとTypstの記事をHTMLに変換しそのままブログとして公開できる形でpublic/ディレクトリに出力します。npx -y pagefind --site publicを実行するとpagefindによる検索も可能です。これらのセットアップを行うGitHub Actionsのdeploy.ymlも同梱しています。
Typstでの執筆
記事はディレクトリごとに index.typ を置く形です。
記事の先頭はこのような感じになります。
#import "/template.typ": article, calver, post-meta
#let meta = post-meta(
slug: "my-first-post",
title: "My First Post",
create: calver(2026, 1, 1),
description: "記事の短い説明文です。meta.descriptionになります。",
tags: ("Typst",),
draft: false,
)
#metadata(meta) <post-meta>
#show: article.with(..meta)
= はじめに
本文を書きます。
/template.typ はルートにある互換用の入口です。記事側ではcore配下のファイルを直接importせず、このtemplate.typからarticle、calver、post-metaなどを使う想定にしています。
slug が公開URLになり、title や description は記事ページ、記事一覧、RSS、OGPなどに使われます。tags を付けておくとタグページにも反映されます。
draftを指定しないかdraft: true が指定された記事は、HTML出力、記事一覧、RSS、sitemapから除外されます。まだ途中の記事をリポジトリには置いておきたいけれど公開はしたくない、というときに使えます。公開する記事ではdraft: falseを指定してください。
post-metaは他にも共有時に表示される画像(og-image)や記事に書く長めの概要(abstract)などの色々な引数を受け付けます。
これらの設定を書いたらあとはそのままいつものようにTypstでブログを書けます。
ビルド
python3 build.pyで主に次のものを生成します。
- 各記事のHTML
- トップページの記事一覧
- タグ一覧ページ
- タグ別の記事一覧ページ
- RSSフィード
- sitemap
- 404ページ
- 記事ごとの画像などの静的アセット
ルートのbuild.pyは、submoduleとして取り込んだvendor/typst-blog-core/build.pyを読み込んで実行します。普段はルートで次のコマンドを実行すれば大丈夫です。
python3 build.py
Typst本体は現時点ではHTMLを単一ファイルとして出力します。
このテンプレートでは記事ごとにTypstをコンパイルし、public/ 配下に静的サイトとして配置することでブログとして扱いやすい構成にしています。記事ディレクトリ内に置いた画像、PDF、BibTeX/Hayagrivaファイルなども記事の出力先へコピーされます。
core側に同梱しているCSS、JavaScript、テーマ、robots.txtなどもpublic/へコピーされます。自分のサイト固有の画像、favicon、追加CSS、独自テーマ、CNAMEなどはルート側のstatic/に置けます。
GitHub Pagesですぐ公開可能
GitHub Pages向けのWorkflowも同梱しています。
テンプレートからリポジトリを作り、ローカルにcloneするときはsubmoduleも一緒に取得します。
git clone --recurse-submodules https://github.com/USER/REPO.git
cd REPO
すでにclone済みでvendor/typst-blog-coreが空の場合は、次を実行してください。
git submodule update --init --recursive
その後、site.typ を自分のサイト用に編集し、GitHub PagesのSourceをGitHub Actionsに設定すればmain ブランチへのpushで公開できます。
Workflowでは、おおまかに次の処理を行います。
python3 build.py
npx -y pagefind --site public
GitHub Actions側ではactions/checkoutのsubmodules: recursiveを指定しているので、Pagesのビルド時にもcoreが取得されます。生成された public/ がGitHub Pagesにデプロイされます。
ローカルで確認したい場合はビルド後に普通の静的ファイルサーバーでpublic/を配信します。
python3 -m http.server 8000 -d public
Pagefindによるサイト内検索
静的サイト内検索には Pagefind を使っています。
検索インデックスは次のコマンドで生成します。
npx -y pagefind --site public
検索UIはサイドバーとモバイル表示に組み込んでいます。JavaScript側では、検索欄にフォーカスしたタイミングでPagefindを読み込み検索結果を表示します。もちろんGitHub Pagesのような静的ホスティングサイトでもちゃんと動きます。
記事を読むための導線
トップページには記事カードを並べます。記事ページにはタグ、作成日、更新日、概要、目次、著者ウィジェットを表示します。記事下には前後の記事へのリンクと別記事への導線も出します。
タグを付けた記事からは /tags/<tag>/ のようなタグページへリンクされます。タグ一覧ページも生成されるので記事数が増えてきても分類しやすいです。
デスクトップではサイドバーに目次を置き、モバイルでは折りたたみ式の目次を出すようにしています。

テーマとフォントの設定
テーマはCSS変数で管理しています。
初期状態ではダークテーマとライトテーマを同梱しています。
site.typ の theme を変更すると、読み込むテーマCSSを切り替えられます。
theme: "light"
同梱テーマはcore側のvendor/typst-blog-core/static/themes/にあります。独自テーマを作る場合は、ユーザー側のstatic/themes/my-theme.css のようにCSSファイルを追加し、site.typ 側で theme: "my-theme" を指定します。
フォント設定も site.typ にまとめています。本文、見出し、コード、数式、任意の追加フォントを設定できます。Web用フォントはGoogle Fontsから読み込むようにしています。
fonts: (
main: (
pdf: "Noto Serif CJK JP",
web: "Noto Serif JP",
weights: "400;700",
fallback: "serif",
),
code: (
pdf: ("Fira Code", "Consolas", "monospace"),
web: "Fira Code",
weights: "300..700",
fallback: "monospace",
),
)
Typst側で使うフォントとWeb側で読み込むフォントを同じ設定に寄せられるので、PDF出力とHTML出力を両方意識したい場合にも扱いやすいと思います。
OGP、RSS、sitemapなどの生成
このテンプレートでは記事メタデータとサイト設定を元にRSSとsitemapを生成します。
各ページの head にはdescriptionやOGP用のメタタグも入れています。
site.typ の github_repo にリポジトリURLを設定しておくと、記事ページからその記事の編集履歴へ移動できるリンクも表示されます。
Cloudflare Web Analyticsを使う場合はsite.typ にトークンを設定するとスクリプトを読み込むようにしています。
不要なら none のままで大丈夫です。
analytics: (
cloudflare_token: none,
)
記事への感想や誤字報告を受け取りたい場合に備えてGoogleフォームへのフィードバック導線も入れています。フォームURLとentry IDを設定すると記事タイトル付きでフォームを開けるようにしています。
X、Misskey、タイトルと概要のコピー用ボタンも設定できます。
share: (
x: true,
misskey: true,
copy: true,
)
補助パーツ一覧
このテンプレートでは、次のような部品を用意しています。
notetipimportantwarningcautionyoutuberaw_html
例えば補足や注意を書きたいときは次のように書けます。
#note[
これは補足です。
]
#warning[
ここは注意して読んでください。
]

YouTube埋め込みはURLまたは動画IDからiframeを出せます。
また、HTMLをそのまま埋め込みたい場合はraw_html が使えます。
== サイトの埋め込み
#raw_html(`<iframe width="560" height="315" src="https://www.youtube.com/embed/eWw8HoNkVkU?si=t5T43P_gid2S7B5z" title="YouTube video player (raw HTML)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>`)
#youtube("https://www.youtube.com/watch?v=eWw8HoNkVkU", start: 30)

コードブロックにはコピー用ボタンを付けています。
脚注はクリックまたはホバーで本文中にポップアップ表示します。
参考文献リンクをクリックするとサイドバーや画面下部に該当文献をプレビューします。
脚注をクリックして出てきたテキスト内の引用([1])をクリックしたところ。右側に参考文献 [1]が表示されていることがわかる。
多言語UI
サイトUIの文言は vendor/typst-blog-core/typst/core/i18n.typ にまとめています。
今の所、日本語、英語、韓国語、簡体字中国語、繁体字中国語の文言を入れています。site.typ の language を変えると、戻るボタン、目次、検索、共有、フィードバック、タグページなどのUI文言が切り替わります。
翻訳品質を完璧に保証するというよりはテンプレートとして最低限差し替えやすい場所に集めておくという意図です。coreはsubmoduleとして取り込んでいるので、通常は直接編集せず、必要ならcore側に変更を入れるか、自分用のforkやブランチを参照する運用が良いと思います。
ブログエンジンの更新
このテンプレートではブログを生成する本体を vendor/typst-blog-core として取り込んでいます。記事や site.typ は自分のリポジトリに残したまま生成部分だけをあとから更新できます。
更新するときは、release tag に切り替える運用をおすすめしています。
cd vendor/typst-blog-core
git fetch --tags
git tag --sort=-version:refname
git checkout vYYYY.MM.DD
cd ../..
python3 build.py
npx -y pagefind --site public
git add vendor/typst-blog-core
git commit -m "Update blog core to vYYYY.MM.DD"
vYYYY.MM.DD は実際に使いたい release tag に置き換えてください。更新後はローカルで表示を確認してから push してください。
使い方
必要なものは次の通りです。
- Git
- Typst 0.14.2以上
- Python 3.10以上
- Node.js 20以上
まずこのテンプレートからリポジトリを作ります。
次に、submoduleも含めてローカルにcloneします。
git clone --recurse-submodules https://github.com/USER/REPO.git
cd REPO
すでにclone済みの場合やvendor/typst-blog-coreが空の場合は次を実行してください。
git submodule update --init --recursive
次に site.typ を自分のサイト向けに編集します。最低限次の項目を見れば始められます。
titledescriptionbase_urlgithub_repolanguagethemeauthorshare
記事を書くときは、example-post/index.typ をコピーして新しい記事ディレクトリを作るのが簡単です。
cp -R example-post my-first-post
ビルドします。
python3 build.py
npx -y pagefind --site public
ローカルで確認します。
python3 -m http.server 8000 -d public
GitHub Pagesで公開する場合は、GitHubの Settings からPagesのSourceをGitHub Actionsに設定し、main ブランチへpushします。
注意
TypstのHTMLエクスポートは公式にもまだ実験的な機能として扱われています。
このテンプレートもその上に乗っているものなので今後のTypstの変更で調整が必要になる可能性はあります。特にHTML出力まわりや数式まわりは今後さらに変わっていくはずです。
また、現在のテンプレートはcoreをGit submoduleとして取り込んでいます。clone直後にvendor/typst-blog-coreが空だったり、typst-blog-core submodule is missingのようなエラーが出たりした場合は、次を実行してください。
git submodule update --init --recursive
ただ、個人ブログとして使うにはすでにかなり面白いところまで来ています。MarkdownではなくTypstで文章を書きたい人、Typstの参照や文献管理の書き味が好きな人、HTMLエクスポートを使って何か作ってみたい人はよければ触ってみてほしいです。MITライセンスでGitHubに公開してあります。
参考:
Discussion