ウェブサイトをリニューアルした
突然だが、ウェブサイトをリニューアルした。何を捨てて何を選んだか、その顛末のメモ。
4 年と 1 本
4 年前に「月に 1 本は書くぞ〜」と豪語したのに、結局書けたのは片手で数えるどころか 1 本である。約束は守れない方の人間だ。それでもこのドメインだけは更新料を払い続けてきたので、せめて器ぐらいは綺麗にしておきたい。
ウェブサイトの誕生 のときは、Notion をヘッドレス CMS にして OSS テンプレートに乗っかった。気軽に書ける反面、デザインは「OSS のまま」で、4 年経っても自分のものになっている感じがしなかった。AI に背中を押されたこともあって、今回は一から書き直すことにした。
Notion から、自前のスタックへ
2022 年のときは、Notion をヘッドレス CMS にして OSS テンプレ(nobelium)に乗っかっていた。記事は Notion 上のブロックで書き、フロントエンドは「OSS のまま」で動いている、という構成。
今回はそこを全部捨てた。テンプレートには乗らず、Next.js 16 (App Router) + React 19 + TypeScript でゼロから組み、Tailwind CSS v4 で見た目を当てた。
Notion から書き出していた頃は、フロント側のことを「いじれない箱」として扱っていた。その箱を全部開けて、自分の手で組み直した感がある。
記事の管理
記事は content/posts/*.mdx に MDX で置く。リニューアル直後は velite を使っていたが、2026 年 5 月に Next.js 純正の @next/mdx へ移行した。
コードハイライト
rehype-pretty-code + Shiki でビルド時にトークン化。ファイル名やフッターの挿入は自前の rehype プラグイン (lib/rehype-code-footer.mjs) で処理している。
埋め込み
- ツイート: react-tweet
- 外部 URL: OGP を
node-html-parserで取得して自前のカードコンポーネントで描画
リアクション (Like)
ストレージは Vercel KV、レート制限は Upstash Ratelimit。
ホスティング / アナリティクス
Vercel + @vercel/analytics。
CI / Lint / Format
GitHub Actions で biome check → tsc --noEmit → next build の直列。Lint と format は Biome。
フィード / サイトマップ / OG
app/feed.xml (Atom)、app/feed.json (JSON Feed)、app/sitemap.ts / app/robots.ts、OG 画像は app/api/og-lab/ で動的生成。
構造化データ
JSON-LD で Person / WebSite / Organization を /about と app/layout.tsx から提供。
アイコン・アニメーション
- アイコン: lucide-react + react-icons
- アニメーション: motion (旧 framer-motion v12)
ヘッダーをやめた
リニューアルで一番悩んだのはヘッダーだ。サイトの一番上にナビがあるのは「礼儀」みたいなもので、無くすと不安になる。けれど、ロゴ+メニューが乗っていると、それだけで一気に「業務的」になってしまう。
割り切ってヘッダーを完全撤去し、Notes / About / RSS は全部フッターに移した。何もない上の余白の方が、自分にとっては大事だった。
葉影、シェーダーから動画へ
トップに置きたかったのは、白い壁の上で葉影がゆっくり揺れる背景だった。最初はかっこつけて WebGL から書こうとした。
ogl で fragment shader を書き、fbm ノイズで葉のシルエットを procedural に生成しようとした。出力は「煙」になり、パラメータをいじると今度は「顕微鏡で見た微生物」になった。手を動かすほど主張が強くなって、侘び寂びから遠ざかっていく。
途中で procedural に作るのは諦めた。実物の葉影が風で揺れている動画を、<video> で mix-blend-mode: multiply で薄く重ねるだけ、に切り替えた。
// components/leaf-shadow-video.tsx (抜粋)
<video
src="/video/leaf-shadow.mp4"
autoPlay muted playsInline loop
style={{
objectFit: "cover",
mixBlendMode: "multiply",
opacity: 0.3,
}}
/>素材は Pexels で見つけた CC0 の葉影動画を、ffmpeg で 720p / グレースケール / シームレスループ加工した 400KB ほどの MP4。xfade で末尾と先頭を 1.5 秒重ねるとループの継ぎ目がほぼ見えなくなる。
ffmpeg -i leaf-cut.mp4 -filter_complex \
"[0:v]split=2[a][b];[b]trim=start=0:end=1.5,setpts=PTS-STARTPTS[head];\
[a][head]xfade=transition=fade:duration=1.5:offset=8.0[v]" \
-map "[v]" -an -c:v libx264 -crf 28 -movflags +faststart \
public/video/leaf-shadow.mp4WebGL で書いていた数時間が無駄だったかというと、そうでもない。procedural では出せない領域を、自分の手で一度通ったからこそ、「実物の動画を薄く重ねるだけ」という地味な解に 納得して たどり着けた。最初から動画にしていたら、たぶん「もっとシェーダーで頑張れたんじゃないか」が頭の片隅に残り続けたと思う。
月に 1 本
「月に 1 本」は前回も果たせなかった。器がきれいになったところで、書く頻度が劇的に変わるかというと、たぶんそんなことはない。
正直なところ、Notion からローカル MDX に移したのは、書きやすさのためではない。Notion は今も日常で使っていて、書くこと自体の腰の重さは元々ない。むしろ git にコミットして push する MDX のほうが、操作としては面倒だ。書く/書かないをツールの摩擦のせいにするのは嘘で、自分の場合は メンタリティ の方が大きい。
承認欲求が先に立つと、書くためのインプットさえ「アウトプット起点」になりやすい。先に「出したい」「認められたい」があって、それを満たすための材料探しとして本を読み、コードを書き、世界を眺める。順序が逆になると、書けるものは増えても、書きたいものは増えない。
哲学者の韓炳哲(ハン・ビョンチョル)は 『疲労社会』 で、現代を 「自己搾取 (Selbstausbeutung)」 の時代と呼んだ。外から駆り立てられるのではなく、自由なはずの自己が、自分自身に「もっと出せ」と要求し続ける構造になっている、という診断だ。承認欲求は、内面化された監督官として勝手に鳴り続ける。
アラスデア・マッキンタイアの言い方を借りれば、書くという「実践 (practice)」には 内的善(書くこと自体に内在する価値)と 外的善(承認・名声)の二種類があり、後者を目的にした瞬間に前者が痩せる(『美徳なき時代』)。書きたいから書く、ではなく、認められたいから書く、に動機がすり替わった瞬間に、書くこと自体は空洞化する。
ともあれ、器はできた。次に問われるのは、ツールではなく自分のほうだ。