このブログにはカテゴリページとカテゴリサイドバーを追加していました。

機能としては十分だったのですが、カテゴリ名だけが並んでいると少し寂しく見えます。
AstroPaper系のシンプルな見た目は維持したかったので、装飾を増やすというより、カテゴリ名の横に小さな線画アイコンを添える方向で調整しました。
今回は、そのときに使ったastro-iconと、Cloudflare Pagesのデプロイで詰まったpnpm-lock.yamlの話をメモしておきます。
カテゴリ名だけだと少し寂しい
対象にしたのは、次の3か所です。
- ヘッダー下のカテゴリ一覧
/categories/のカテゴリ一覧- サイドバーのカテゴリ一覧
カテゴリはテキストだけでも意味は伝わります。
ただ、ヘッダー下やサイドバーのように小さな導線として置く場合、同じような文字列が並ぶだけだと少し平坦に見えます。
そこで、カテゴリごとにアイコンを割り当てることにしました。
今回意識したのは、アイコンを目立たせすぎないことです。
AstroPaperの良さは、余白があり、文字を読みやすいシンプルなデザインだと思っています。
そのため、アイコンは小さめにして、色もテキストより少し控えめにしました。
astro-iconを使うことにした
Astroでアイコンを使う方法はいくつかあります。
SVGを直接置いてimportする方法もありますし、アイコン用のコンポーネントを自分で作る方法もあります。
今回はastro-iconを使いました。
理由は、Iconifyのアイコンセットをそのまま扱えるためです。
たとえば、Lucide Iconsのgamepad-2を使いたい場合、次のように書けます。
---
import { Icon } from "astro-icon/components";
---
<Icon name="lucide:gamepad-2" />
SVGファイルを個別に管理しなくても、nameでアイコンを指定できます。
カテゴリのように、データ側にアイコン名を持たせたい場合とも相性が良いです。
今回は、モノクロ線画ベースにしたかったので、アイコンセットはLucide Iconsにしました。
インストールとAstro設定
まず、astro-iconとLucideのIconifyデータを追加します。
npm install astro-icon @iconify-json/lucide
次に、astro.config.tsへintegrationを追加します。
import icon from "astro-icon";
export default defineConfig({
integrations: [
icon(),
// 既存のintegration
],
});
これで、Astroコンポーネント内からIconを使えるようになります。
---
import { Icon } from "astro-icon/components";
---
カテゴリ定義にアイコン名を持たせる
このブログでは、カテゴリ情報をsrc/data/categories.tsにまとめています。
そこへiconを追加しました。
export const CATEGORIES = {
game: {
name: "ゲーム",
description: "レースゲームやシミュレーターなど、ゲーム関連の記事。",
icon: "lucide:gamepad-2",
order: 10,
featured: true,
},
tech: {
name: "Tech",
description: "技術やブログ運営に関する記事。",
icon: "lucide:pen-square",
order: 20,
featured: true,
},
etc: {
name: "その他",
description: "雑記や分類しづらいメモの記事。",
icon: "lucide:folder",
order: 99,
featured: true,
},
} as const;
もともと想定していたカテゴリと、現在このブログに存在するカテゴリは少し違います。
今回の時点では、実際に存在するカテゴリはgame、tech、etcでした。
そのため、gameにはlucide:gamepad-2、techにはブログ運営寄りとしてlucide:pen-square、etcには汎用的なlucide:folderを割り当てました。
未定義カテゴリが出てきた場合も壊れないように、フォールバックもlucide:folderにしています。
表示側でIconを使う
ヘッダー下のカテゴリ一覧では、カテゴリ名の前に小さくアイコンを表示しました。
<a class="flex items-center gap-1.5 rounded border px-3 py-2">
<Icon name={item.icon} class="size-3.5 flex-none opacity-75" />
<span>{item.name}</span>
</a>
サイドバーでは、カテゴリ名と件数を左右に分けているので、左側だけをアイコン付きのグループにしました。
<span class="flex min-w-0 items-center gap-2">
<Icon name={item.icon} class="size-4 flex-none opacity-75" />
<span class="truncate">{item.name}</span>
</span>
<span class="text-xs opacity-80">{item.count}</span>
/categories/のカードでも、見出しの横に同じアイコンを表示しています。
<h2 class="flex items-center gap-2 text-xl font-semibold text-accent">
<Icon name={category.icon} class="size-4 flex-none opacity-75" />
{category.name}
</h2>
アイコンサイズは、ヘッダー下ではsize-3.5、カテゴリ一覧とサイドバーではsize-4にしました。
大きくすると装飾感が強くなるので、テキストの補助に見える程度にしています。

Cloudflare Pagesのデプロイで失敗した
実装後、ローカルではビルドが通りました。
しかし、Cloudflare Pagesのビルドで失敗しました。
ログには、次のような内容が出ていました。
Detected the following tools from environment: pnpm@10.11.1, npm@10.9.2, nodejs@22.16.0
specifiers in the lockfile don't match specs in package.json
原因は、package.jsonとpnpm-lock.yamlの不一致でした。
今回、依存追加にnpm installを使っていたため、package-lock.jsonは更新されていました。
一方で、Cloudflare Pagesはpnpm@10.11.1を検出し、CI環境なのでpnpm install --frozen-lockfileとして動きます。
そのため、package.jsonにはastro-iconと@iconify-json/lucideがあるのに、pnpm-lock.yamlにはまだ反映されていない状態になっていました。
--frozen-lockfileではlockfileの更新が許可されないため、その時点でインストールが止まります。
対応したこと
Cloudflare Pagesと同じpnpm@10.11.1でlockfileを更新しました。
npx pnpm@10.11.1 install --lockfile-only
その後、同じバージョンでfrozen lockfileのチェックも通ることを確認しました。
npx pnpm@10.11.1 install --frozen-lockfile --lockfile-only
これで、pnpm-lock.yamlにastro-iconと@iconify-json/lucideが反映され、Cloudflare Pagesのビルドも通るようになりました。
今回の直接原因は、アイコンを追加したことではありません。
アイコン追加に伴って依存パッケージを増やしたのに、Cloudflareが使うpnpm-lock.yamlを更新していなかったことが原因です。
まとめ
カテゴリ表示に小さなアイコンを足すだけでも、導線として少し見やすくなりました。
astro-iconを使うと、Lucide IconsのようなIconify対応アイコンをAstroコンポーネントから扱いやすくなります。
一方で、デプロイ環境がpnpmを使っている場合は、依存追加時にpnpm-lock.yamlも忘れずに更新する必要があります。
今回は、見た目の小さな改善でしたが、ローカルのパッケージマネージャーとCIのパッケージマネージャーを揃える大事さも再確認できました。


