ポートフォリオに /blog を足しました。
技術記事は Zenn にも書いているのですが、そちらでは書けないものを置く場所が欲しくなったからです。
書けないもの、というのは表現のことです。 「このテストは実行にこれくらい時間がかかっていて、こう変えたらこうなった」みたいな話は、 文章と表で説明するより、動く図を1つ置いたほうが伝わることがあります。 自分でソースを持っているサイトなら、記事ごとに好きなものを書けます。
仕組み
表現の自由度を最優先にしたかったので、Markdown や MDX ではなく
記事1本 = 1つの .astro ページ にしました。
記事ファイルの中に <script> も <style> も SVG も直接書けます。
ただ、そうすると一覧ページや絞り込みに必要なメタデータ(タイトル・日付・カテゴリ)を 集める方法が要ります。ここは素直に、メタデータだけを別ファイルに分けました。
src/data/blog.ts に1行足す
id・タイトル・日付・カテゴリを書く。ここが記事メタデータの唯一の情報源になる。
src/pages/blog/{id}.astro を書く
自分の id で getPost() を引いてレイアウトに渡したら、あとは中身を自由に書くだけ。
一覧とカテゴリページは自動で追従する
/blog と /blog/category/{id} は blog.ts だけを読んでいるので、記事側では何もしなくていい。
メタデータはここに集約する
// src/data/blog.ts
export const posts: Post[] = [
{
id: 'hello-blog', // = /blog/hello-blog
title: 'このブログの作り方',
description: '...',
publishedAt: '2026-08-03',
categories: ['astro', 'dev'],
},
] 記事本体は普通の .astro ページ
// src/pages/blog/hello-blog.astro
import BlogArticle from '../../layouts/BlogArticle.astro'
import BarChart from '../../components/blog/BarChart.astro'
import { getPost } from '../../data/blog'
const post = getPost('hello-blog')
---
<BlogArticle post={post}>
<p>本文。ここから先は普通の .astro なので、</p>
<BarChart data={[...]} caption="好きな図をそのまま置ける" />
</BlogArticle>
一覧ページが記事コンポーネントを import しない、という点だけは意図的です。
もし一覧側で全記事を読み込んでメタデータを取り出す作りにすると、
記事ごとに書いた <script> が一覧ページのバンドルにまで入ってきてしまいます。
メタデータを別ファイルに切った理由の半分はこれです。
記事の中で使えるもの
よく使いそうな図は src/components/blog/ にコンポーネントとして置いています。
まだ最低限だけで、書きながら足していく予定です。
棒グラフ
数値の比較用。スクロールして画面に入ったタイミングでバーが伸びます。
手書きの図
決まった形に収まらないものは <Figure> に SVG をそのまま入れます。
下の図は線が引かれるアニメーションを CSS で書いたものです。
補足ブロック
これから
今のところ置いてあるのは棒グラフ・手順図・補足ブロック・コード・自由な図の5つだけです。 記事を書いていて「これが欲しい」と思ったものを、その都度足していこうと思っています。
普段は Playwright での E2E まわりを触っているので、そのあたりの話から書いていく予定です。