【実験ログ】1つのAstroテンプレートで4サイトを量産した設計戦略
同じようなサイトを複数作るとき、毎回ゼロから作るのは大変ですよね。かといって、1つのサイトに全部詰め込むのも違う。ジャンルが違えば、デザインも読者層も異なります。
この実験ログでは、1つのAstro(Webサイトを作るための道具。表示が速いのが特徴)テンプレートを作って、それをコピーして4つの異なるサイトを立ち上げた全過程を書いていきます。何を共通にして、何をサイトごとに変えたのか。どこで迷って、どこで失敗したのか。そしてこのやり方の限界と、将来の改善案まで。
テンプレートの使い回しというと「かんたんそう」に聞こえますが、実際にやってみると設計上の判断ポイントが多いです。この記録が、同じことをやりたい誰かの参考になれば嬉しいです。
課題: サイトを作るたびに同じ作業を繰り返すのはツラい
最初にtrip-journal.netを作ったとき、約5時間かかりました。レイアウト、パーツ、CSS、公開設定…全部ゼロから作ったからです。
次のサイトも同じ5時間かかるとしたら、4サイトで20時間。これは個人の副業としてはキツいです。しかも4サイトの構造はほぼ同じ。ヘッダー、フッター、記事カード、カテゴリ一覧…共通する部分がめちゃくちゃ多いんです。
じゃあ、何が違うのか。冷静に分析してみました。
「変わるもの」と「変わらないもの」を分けよう
- 変わらないもの(共通パーツ): ヘッダー、フッター、カード、Callout、ベースレイアウト、ビルド設定、Tailwind設定
- 変わるもの(サイトごとに違う部分): サイト名、色、カテゴリ構成、ナビゲーション、トップページのメインビジュアル、記事コンテンツ
変わるものを1つのファイルにまとめれば、そのファイルだけ書き換えるだけで別サイトになる。
つまり、これがsite.tsアーキテクチャの出発点です。
時間の節約効果
ゼロから作る場合: 5時間 x サイト数
テンプレート方式: 5時間(初回だけ) + 1〜2時間 x 残りのサイト数
4サイトの場合、ゼロからなら20時間。テンプレート方式なら5 + 2 x 3 = 11時間。約45%の時間削減です。サイトが増えれば増えるほど、テンプレート方式の有利さは増していきます。
テンプレートの全体像
テンプレートの構造は、「共通パーツの層」と「サイトごとに違う層」の2層になっています。
共通パーツには、サイト名や色を直接書き込んでいません。サイト名はsite.tsから、色はCSS変数から取得するようにしています。だからコピーしても修正なしでそのまま動くんです。
サイトごとに違う層は、4つのファイル(または4つの領域)に限定されています。新しいサイトを作るとき、触るのはここだけです。
設計の中核: site.ts を詳しく見てみよう
すべての「サイトごとに違う情報」をsrc/config/site.tsというファイルに集約しました。かんたんに言うと、「このファイルがサイトの設定書」です。ここを書き換えるだけで、別のサイトに早変わりします。
site.tsの中身
// src/config/site.ts
export const siteConfig = {
// ── 基本情報 ──
name: "AI Cowork Lab",
tagline: "AIと一緒に、仕事をもっと面白く。",
description: "Claude Code・AI業務活用の実践ガイド。業務効率化からコード生成まで、AIとの協働を実験・検証する。",
url: "https://ai-cowork-lab.com",
// ── 色 ──
colors: {
primary: "#0d9488", // teal-600(メインカラー)
primaryDark: "#0f766e", // teal-700(メインカラーの暗い版)
accent: "#f59e0b", // amber-500(アクセントカラー)
accentDark: "#d97706", // amber-600(アクセントカラーの暗い版)
},
// ── ナビゲーション(メニューに何を並べるか) ──
nav: [
{ label: "Claude Code", href: "/category/claude-code" },
{ label: "業務活用", href: "/category/業務活用" },
{ label: "実験ログ", href: "/category/実験ログ" },
],
// ── カテゴリ設定 ──
categories: {
"claude-code": {
title: "Claude Code",
description: "基本操作から実践テクニックまで",
layout: "steps", // 学習系: 順番に読んでいくスタイル
icon: "terminal",
},
"業務活用": {
title: "業務活用",
description: "日常業務でのAI活用事例",
layout: "grid", // メディア系: 好きなものから読むスタイル
icon: "briefcase",
},
"実験ログ": {
title: "実験ログ",
description: "試したこと・学んだことの記録",
layout: "grid",
icon: "flask",
},
},
// ── フッター ──
footer: {
copyright: "AI Cowork Lab",
links: [
{ label: "プライバシーポリシー", href: "/privacy" },
{ label: "お問い合わせ", href: "/contact" },
],
},
};
site.tsで何をコントロールしているか
- サイト名・キャッチコピー
- URL・SNSシェア設定
- サイト説明文
- メインカラー
- アクセントカラー
- 暗めバリアント
- ナビゲーション項目
- カテゴリ名・説明・アイコン
- カテゴリの表示スタイル(グリッド/ステップ)
- フッターのリンク
- 著作権表示
- SEO初期値
共通パーツからsite.tsをどう使っているか
具体例を見てみましょう。ヘッダーのパーツはこんな感じです。
---
// Header.astro
import { siteConfig } from '../config/site';
---
<header class="bg-primary text-white">
<nav>
<a href="/" class="font-bold text-xl">{siteConfig.name}</a>
<ul>
{siteConfig.nav.map(item => (
<li><a href={item.href}>{item.label}</a></li>
))}
</ul>
</nav>
</header>
ここがポイントなんですが、Header.astroの中に「AI Cowork Lab」とか「Claude Code」といった文字列が一切書かれていません。全部site.tsから読み込んでいます。つまり、site.tsを書き換えるだけで、ヘッダーの表示内容が自動的に変わるということです。
CSS変数で色を管理する ── Tailwind CSS v4の活用
Tailwind CSS(デザインをかんたんに当てられるCSSの道具)v4では@themeという仕組みでCSS変数を定義できます。これを使って、site.tsの色をサイト全体に反映する仕組みを作りました。
global.cssの色の設定
@import "tailwindcss";
@theme {
/* サイトごとに変更する部分(ここだけ変える) */
--color-primary: #0d9488; /* teal-600 */
--color-primary-dark: #0f766e;
--color-accent: #f59e0b; /* amber-500 */
--color-accent-dark: #d97706;
/* 共通の色(変更不要) */
--color-text: #1e293b;
--color-text-light: #64748b;
--color-bg: #ffffff;
--color-bg-alt: #f8fafc;
--color-border: #e2e8f0;
}
サイトごとに変えるのは--color-primaryと--color-accentの4行だけです。Tailwind v4ではこのCSS変数がそのままクラス名として使えるので、bg-primary、text-accent、border-primaryといった書き方がそのまま動きます。かんたんに言うと、4行変えるだけでサイト全体の色が変わるということです。
4サイトのカラーパレット
色の選び方にもルールを作りました。メインカラーは「落ち着いた色(濃いめ)」、アクセントカラーは「目を引く色(明るめ)」を組み合わせるようにしています。メインカラーはヘッダーやリンクに使われて、アクセントカラーはボタンやハイライトに使われるので、この組み合わせが見やすさとバランスを取ってくれます。
カテゴリページの表示を切り替える ── サイトの性質で変える
全サイトで同じカテゴリページパーツ(CategorySection)を使っていますが、表示のしかたはサイトの性質に応じて切り替えています。ここがテンプレート設計で一番悩んだポイントの一つです。
パターン1: カードグリッド(ニュース・メディア系サイト向け)
記事タイトル1
記事タイトル2
記事タイトル3
パターン2: ステップカード(学習・入門系サイト向け)
1 Step 1: バイブコーディングとは?
2 Step 2: 環境構築
3 Step 3: 最初のプロジェクト
どっちを使うかの判断基準
この2パターンの使い分けには、はっきりした基準があります。
- グリッド表示: 記事同士に読む順番がない場合。どの記事から読んでも大丈夫なコンテンツ。ニュース、レビュー、事例紹介など
- ステップ表示: 記事に学習順序がある場合。前の記事を読んでないとわかりにくいコンテンツ。チュートリアル、入門ガイド、ロードマップなど
切り替えはsite.tsのcategories設定でlayout: "steps" か layout: "grid"を書くだけ。パーツ側が自動で判断してくれます。
// CategorySection.astro の切り替えロジック
const categoryConfig = siteConfig.categories[slug];
const layout = categoryConfig?.layout || "grid";
// 表示スタイルに応じて並び順も変わる
const sorted = layout === "steps"
? posts.sort((a, b) => (a.data.order ?? 99) - (b.data.order ?? 99))
: posts.sort((a, b) => b.data.date.getTime() - a.data.date.getTime());
並び順が違う点に注目してください。ステップ表示ではorderで明示的に順番を決めて、グリッド表示では新しい日付が先に来ます。この小さな切り替えが、サイトの使い勝手に大きく影響するんです。
同じサイトの中でもパターンを混ぜて使える
ai-cowork-lab.comでは「Claude Code」カテゴリはステップ表示(入門→応用の順序がある)、「実験ログ」カテゴリはグリッド表示(新着順で好きなものを読む)にしています。つまり、サイト単位じゃなくてカテゴリ単位で切り替えられるのが、この設計の強みです。
4サイトの構成比較
テンプレートから生まれた4サイトを一覧で比較してみましょう。
| サイト | テーマ | メインカラー | アクセントカラー | カテゴリ数 | 表示スタイル | 記事数 |
|---|---|---|---|---|---|---|
| trip-journal.net | 旅行・おでかけ | Navy #1a365d | Red #e53e3e | 2(国内/海外) | グリッド | 11本 |
| vibe-code-lab.com | バイブコーディング入門 | Purple #6b21a8 | Green #16a34a | 3(入門/ツール/実践) | ステップ | 15本 |
| ai-cowork-lab.com | AI業務活用 | Teal #0d9488 | Amber #f59e0b | 3(Claude Code/業務活用/実験ログ) | 混在 | 9本 |
| ceo-tax.com | 経営者向け税金 | Blue #1e40af | Red #ef4444 | 3(税金/節税/経理) | グリッド | 15本 |
4サイトのジャンルはバラバラですが、技術的な仕組みは完全に同じです。Astro + Tailwind CSS v4 + Cloudflare Pages(GitHubと連携してサイトを無料で公開できるサービス)。この統一性が、運用をラクにしてくれています。
パーツの使い回しを検証する
そのまま使えたパーツ(修正ゼロ)
テンプレートをコピーしたとき、一行も直さずにそのまま動いたパーツたちです。
| パーツ | 役割 | なぜ修正不要だったか |
|---|---|---|
Callout.astro | 注意書きやポイントのボックス | サイト固有の色を使ってないから |
Card.astro | 記事カードの表示 | CSS変数でサイトの色が自動で変わるから |
Footer.astro | フッター | site.tsの設定を読んでいるから |
Header.astro | ヘッダー・メニュー | site.tsのメニュー設定を読んでいるから |
CategorySection.astro | カテゴリ別記事一覧 | site.tsのカテゴリ設定を見て動くから |
BaseLayout.astro | 共通レイアウト(head, metaタグ) | SEO設定もsite.tsから読むから |
Sidebar.astro | サイドバー | site.tsのカテゴリから動的に生成するから |
7つのパーツが修正ゼロで動きました。これは、設計の段階で「site.tsかCSS変数からしか値を取らない」というルールを徹底した成果です。つまり、パーツの中にサイト名や色を直接書き込まなかったおかげで、コピーするだけで別サイトの顔になってくれたということです。
カスタマイズが必要だったパーツ
| パーツ | 理由 | 何をしたか |
|---|---|---|
index.astro(トップページ) | サイトのコンセプトごとにHTMLの構造自体が違う | メインビジュアル、ボタン、セクション構成を全面書き換え |
SpotCard.astro | 旅行サイト専用のお店紹介パーツ | 他サイトでは削除 |
Timeline.astro | 旅行サイト専用の行程表示パーツ | 他サイトでは削除 |
使い回しやすいパーツを作るコツ
パーツの中にサイト固有の文字列や色を直接書かないこと。必ずsite.tsかCSS変数を経由して取得するようにします。これだけで、コピー時の修正箇所が激減します。「このパーツを別のサイトにコピーしてもちゃんと動くかな?」と常に自分に問いかけるのが、良い設計につながります。
トップページの問題 ── 唯一の手動カスタマイズが必要な場所
ヘッダー、フッター、記事ページは全サイト共通のパーツで動きます。でもトップページだけは、サイトごとにがっつり書き換える必要がありました。
旅行サイトのトップページ
- 大きなメインビジュアル + 「旅の記録を、もっと身近に。」のコピー
- 地域別のカード(国内/海外)
- 最新記事一覧
学習サイトのトップページ
- ロードマップ型のステップ表示
- 「Step 1 → Step 2 → Step 3」の学習フロー
- カテゴリ別の入り口(入門 / ツール / 実践)
AI活用サイトのトップページ
- 「AIと一緒に、仕事をもっと面白く。」のコピー
- 3カテゴリへの導線(Claude Code / 業務活用 / 実験ログ)
- 最新の実験ログをハイライト表示
税金サイトのトップページ
- 信頼性を重視したシンプルなデザイン
- 「経営者のための税金ガイド」のコピー
- 3カテゴリへの導線(税金 / 節税 / 経理)
トップページの構造はsite.tsだけでは対応しきれません。メインビジュアルのHTML構造自体がサイトごとに違うからです。旅行サイトは画面いっぱいの画像を使い、学習サイトはステップ型のUIを使い、税金サイトはテキスト中心のシンプルな構成。ここは毎回手で書き換える必要がありました。
改善案: トップページもパターン化する
// site.ts にトップページのタイプを追加する案
topPage: {
type: "media" | "learning" | "blog",
hero: {
title: "AIと一緒に、仕事をもっと面白く。",
subtitle: "Claude Code・AI業務活用の実践ガイド",
image: "https://images.unsplash.com/...",
cta: { label: "始める", href: "/category/claude-code" },
},
}
こうすれば、トップページのパーツ内でtypeに応じて自動で表示を切り替えられます。media型はカードグリッド、learning型はステップフロー、blog型は最新記事一覧。かんたんに言うと、トップページも設定ファイルだけで変えられるようにしよう、という案です。次の改善で実装予定です。
コピー運用のリアルと限界
コピーでうまく回っている理由
今のところ4サイトなら、コピー運用で問題なく回せています。その理由は:
- サイト間でパーツの違いがほぼない ── 使い回しやすい設計ができているので、7割のパーツが修正不要
- バグ修正の頻度が低い ── 安定稼働に入れば、テンプレートの修正はまれ。週に1回あるかないか
- 4サイト程度なら手動コピーで十分 ── 10分で全サイトに修正を反映できる
コピーが厳しくなる条件
でも、以下のどれかに当てはまったら、コピー運用は限界を迎えます。
- サイト数が10以上になる ── 手動コピーの手間が無視できなくなる。1箇所の修正を10サイトにコピーするのは、ミスの温床
- 共通パーツの更新が頻繁になる ── 週3回以上の修正が入ると、コピー作業だけで毎週1時間消費する
- サイトごとの独自カスタマイズが増える ── パーツに「このサイトのときだけ」的な分岐が入り始めたら危険信号
- チームで開発する ── 「どのサイトが最新版か」がわからなくなる
「コピーの落とし穴」 ── 実際に起きた問題
Callout.astroの余白を調整したとき、4サイト全部に同じ修正を入れる必要がありました。trip-journalで修正 → vibe-code-labにコピー → ai-cowork-labにコピー → ceo-taxにコピー。この4回のコピーのうち、1回でもファイルを間違えると古いバージョンが残ります。
実際に、ceo-taxにコピーし忘れて、1日気づかなかったことがあります。見た目にはほぼ影響ないレベルの違いでしたが、「いつか大きなバグで同じことが起きたら」と思うと、もっとちゃんとした仕組みが必要だなと感じました。
将来の構想: モノレポ化
コピー運用の限界が見えてきたので、将来のやり方としてモノレポ(複数のプロジェクトを1つのフォルダで管理する方法)を検討しています。
project-root/
├── packages/
│ └── shared/ ← 共通パーツをまとめたフォルダ
│ ├── components/
│ │ ├── Callout.astro
│ │ ├── Card.astro
│ │ ├── Header.astro
│ │ ├── Footer.astro
│ │ ├── Sidebar.astro
│ │ └── CategorySection.astro
│ ├── layouts/
│ │ └── BaseLayout.astro
│ └── package.json
│
├── sites/
│ ├── trip-journal/ ← サイト固有: site.ts + 記事 + トップページ
│ │ ├── src/config/site.ts
│ │ ├── src/content/posts/
│ │ ├── src/pages/index.astro
│ │ └── astro.config.mjs
│ ├── vibe-code-lab/
│ ├── ai-cowork-lab/
│ └── ceo-tax/
│
├── package.json ← 全体の設定
└── turbo.json ← 並列ビルドの設定
モノレポにすると何が嬉しいのか
- 共通パーツを1回直せば全サイトに反映される: コピー漏れが原理的に起きない。これが一番大きいメリット
- 全サイトを一括ビルド: コマンド一発で全部まとめてビルドできる
- パッケージの更新も一元管理: 1回の操作で全サイトの依存関係を更新
- 何が共通で何が固有か一目瞭然: フォルダ構成を見るだけでわかる
モノレポの課題
ただし、モノレポにも課題はあります。
- Astroのパーツをパッケージとして読み込めるか: 技術的に検証が必要
- Cloudflare Pagesのビルド設定: サイトごとにルートフォルダを指定する必要がある
- 移行作業のコスト: 今ある4サイトをモノレポに移す作業自体に半日〜1日かかる
今はコピーで十分
4サイト程度ならコピーで十分回せます。モノレポ化するのは、サイト数が8〜10を超えたときか、パーツの修正が頻繁に必要になったとき。つまり、「今は必要ないけど、将来必要になるかも」という段階です。必要になったときに作ればいいんです。
コピー時のよくあるハマりポイント
これから同じことをやる人のために、実際にハマったポイントを具体的に書いておきます。
ハマり1: node_modulesのコピーは壊れる
一番よく起きる問題です。フォルダをまるごとコピーすると、npm(プログラミングのパーツをインストールするための道具)でインストールしたファイルのリンクが壊れます。
# 必ずやること
rm -rf node_modules
rm -f package-lock.json
npm install
症状: 開発サーバーは起動するけど、特定のファイルの読み込みでエラーが出る。または、ファイルを変更しても自動反映されない、ビルドが途中で止まる、など。
node_modulesのコピーは「静かに壊れる」のが厄介
完全に動かなくなるなら気づきやすいんですが、「80%動いて20%だけおかしい」という状態になりがちです。原因を探すのに時間がかかるので、コピー後は問答無用で削除→再インストールが正解です。
ハマり2: .gitフォルダの衝突
コピー元の.gitがそのまま残ると、GitHub(Gitのデータを保存するクラウドサービス)へのpush時に「もう存在するリポジトリ」としてエラーになります。あるいは、間違えて元のリポジトリにpushしてしまう危険もあります。
rm -rf .git
git init
git remote add origin https://github.com/username/new-site.git
ハマり3: package.jsonの名前を変え忘れる
package.jsonのnameを変え忘れると、ビルドログで「どのサイトのビルドか」が判別しにくくなります。動作に支障はないですが、トラブル時に混乱の元になります。
ハマり4: faviconを変え忘れる
faviconを変更し忘れて、全サイトが同じアイコンになっていたことがあります。ブラウザのタブで見分けがつかない。地味ですが、実際の使い勝手に影響します。
ハマり5: SNSシェア画像のURLがコピー元のまま
レイアウトパーツ内のSNSシェア画像の初期URLを変え忘れると、SNSでシェアしたときにコピー元サイトの画像が表示されてしまいます。site.tsにSNSシェア設定を入れておくことで防げますが、見落としやすいポイントです。
コピー時のチェックリスト
上のハマりポイントをふまえて、コピー時に確認すべきことをまとめました。
rm -rf node_modules .git package-lock.jsonnpm install && git initsite.ts: name, tagline, description, url, colors, nav, categories, footer を全て変更global.css:--color-primaryと--color-accentを変更package.json: name を変更index.astro: トップページのメインビジュアル、ボタン、セクション構成を変更public/favicon.svg: サイト固有のアイコンに差し替えsrc/content/posts/: コピー元の記事を削除npm run devでローカル確認npm run buildでビルドテスト- GitHubに新しいリポジトリを作成 → push
- Cloudflare Pagesに接続 → カスタムドメイン設定
所要時間: 30分〜1時間(記事作成を除く)
このチェックリストに従えば、「動くけど何かおかしい」という状態を防げます。特に1と3は必須。これを飛ばすと確実にハマります。
まとめ: テンプレート設計で得た教訓
7つの設計教訓
- site.tsにまとめる: サイトごとに違う情報は1ファイルに集約する。バラバラにすると修正漏れが起きる
- CSS変数で色を管理: 色を直接書き込むとコピー時に全部直す必要がある。CSS変数なら2行変えるだけ
- パーツは汎用的に作る: site.tsかCSS変数から値を取るルールにする。直接書き込みは使い回しの敵
- トップページだけは例外: 完全にsite.tsで対応するのは難しい。パターンを決めて切り替えるのが現実的
- node_modulesは必ず削除: コピー時の一番よくあるハマりポイント。「動くけど不安定」を防ぐために必須
- コピー運用の限界を知っておく: 4サイトなら大丈夫だけど、10サイトになったらモノレポ化が必要
- 作りすぎない: モノレポは「必要になったとき」に作ればいい。今は動くもので十分
1つのテンプレートから4サイトを展開できたのは、site.tsの設計のおかげです。設計に1時間かけたことで、その後の3サイトそれぞれで3時間以上を節約できました。合計9時間以上の節約。つまり、最初に1時間の投資をしたら、9倍のリターンがあったということです。
テンプレート設計は「未来の自分への投資」です。最初の1つに時間をかけることをケチらないでほしい。そして、完璧を目指しすぎないでほしい。site.tsに全部入れなくても、トップページだけ手で直せばいい。大事なのは「80%を自動化して、20%だけ手を動かす」というバランスです。