きっかけ
42 Tokyoでの学習をアウトプットする場所が欲しかった。
主に自分向けだし、QiitaやZenn、noteに投稿するのは恥ずかしいので、どうせなら自分で作ってみようと思ったのが始まり。
技術スタック
- Next.js 16(App Router) — App Routerベース
- TypeScript — デファクトスタンダード
- Tailwind CSS v4 — スタイリング
- gray-matter — Markdownファイルのフロントマター解析
- unified / remark / rehype — Markdown → HTML変換パイプライン
- rehype-pretty-code + Shiki — コードブロックのシンタックスハイライト
- next-themes — ダークモード対応
構成
app/ # ページ・レイアウト
components/ # Header, Footer, PostCardなど
lib/ # posts.ts(記事取得), mdx.ts(変換パイプライン)
content/ # カテゴリ別サブフォルダに.mdを置く
types/ # PostMeta, Post型定義記事はcontent/以下にカテゴリ別でマークダウンファイルを置くだけ。slugはファイル名から自動生成される。
工夫したポイント
フロントマター
各記事はMarkdownファイルの先頭にYAML形式のフロントマターを書くだけで管理できる。
---
title: "記事タイトル"
date: "2026-03-10"
category: "others"
tags: ["Next.js", "ブログ"]
description: "記事の説明文"
published: true
externalUrl: "https://example.com" # 外部リンク記事の場合
externalLabel: "noteで読む" # リンクのラベル(省略可)
---published: falseにすれば下書き扱いになり一覧に表示されない。
ダークモード
next-themesを使い、ライト・ダーク両対応。CSS変数でアクセントカラーを管理しているので、テーマ切替時にカラーが自動で追従する。
外部リンク対応
noteなどの外部サービスに書いた記事も一覧に載せたかった。フロントマターにexternalUrlを指定すれば同じ見た目のまま外部リンクカードとして扱われる仕組みにした。
目次(Table of Contents)
記事ページに自動で目次を生成する仕組みを追加した。rehype-slugで見出しにidを付与し、unifiedパイプライン内でh2/h3要素を走査して見出し一覧を抽出している。
完全静的生成
/blog/[slug]、/blog/category/[cat]、/blog/tag/[tag]はすべてSSG。ビルド時に全ページを生成するので表示が速い。
デプロイ
ホスティングはVercel。GitHubリポジトリと連携しているので、mainブランチにpushするだけで自動デプロイされる。
ドメインはtsito.meを取得して設定した。Vercelのダッシュボードからカスタムドメインを追加し、DNSレコードを向けるだけでHTTPSも自動で有効になる。
まとめ
短期間でそれなりのものができた。デザインや機能は今後も少しずつ育てていく予定。