なぜ移行したのか
株式会社ゼネフェクトの後藤です。
旧サイトはWordPress(Bitnami)で構築していました。動いてはいたんですが、じわじわとこんな不満が溜まっていました。
- エンジニアが気軽にコンテンツを更新できない(管理画面にログインして、ビジュアルエディタで……というのが地味にだるい)
- PHPの実行環境の保守コストがかかる
- デザインをちょっと変えたいだけなのに、テーマファイルと格闘する羽目になる
要するに「エンジニアのチームなのに、サイト更新がエンジニアリングになっていない」という状態でした。これを解消するために、思い切ってSvelteKit + Tailwind CSS + MDsveXの構成に移行することにしました。
技術選定
| 技術 | 選定理由 |
|---|---|
| SvelteKit | 軽量・高速。TypeScriptとの相性がよく、学習コストも低め |
| Tailwind CSS v4 | ユーティリティファーストでデザインの一貫性を保ちやすい |
| MDsveX | Markdownでコンテンツを管理しつつ、Svelteコンポーネントも埋め込める |
| Vercel | ゼロコンフィグデプロイ。PRごとのプレビュー環境が便利 |
| Bun | npm互換で高速。bun install の速さに慣れるともう戻れない |
選定にあたって一つ意識したのは、Claude Codeでプロジェクトを始めるとデフォルトでReactになりがちという点でした。Reactを採用することもできましたが、SvelteKitはすでに現在進行中の販売管理システムのリニューアルで使っており、実際にコードがシンプルになることを確認できていたので、迷わずSvelteKitを選びました。
コーポレートサイトは基本的に静的コンテンツが中心なので、SPAフレームワークほどの複雑さは不要です。SvelteKitはその「ちょうどいい軽さ」がフィットしました。
Markdownでコンテンツを管理する
今回のサイトでは、ブログ記事・実績・メンバー情報をすべてMarkdownファイルで管理しています。
content/
├── blog/
│ └── 2026-05-08-wordpress-to-sveltekit.md
├── works/
│ └── project-name.md
└── members/
└── member-name.md 記事を追加するときは、Markdownファイルを1つ作ってpushするだけ。WordPressの管理画面を開く必要はありません。
MDsveXとレイアウトの自動適用
MDsveXを使うと、Markdownファイルを直接Svelteコンポーネントとして扱えます。svelte.config.js でブログ記事にレイアウトを自動適用する設定をしています。
mdsvex({
extensions: ['.md', '.svx'],
layout: {
blog: 'src/layouts/BlogPost.svelte'
}
}) content/blog/ 配下のMarkdownは自動で BlogPost.svelte レイアウトが適用されるので、記事ごとにレイアウトを指定する必要がありません。レイアウト側では Tailwind の Typography プラグインを使って、Markdownのレンダリングをいい感じに整えています。
import.meta.glob で記事を動的に読み込む
ブログの一覧ページでは、Viteの import.meta.glob を使って content/blog/*.md を一括で読み込んでいます。
const modules = import.meta.glob('/content/blog/*.md', { eager: true });
const posts = Object.entries(modules).map(([path, mod]) => ({
slug: path.split('/').pop()?.replace('.md', '') ?? '',
title: mod.metadata?.title ?? '',
description: mod.metadata?.description ?? '',
publishedAt: mod.metadata?.publishedAt ?? '',
tags: mod.metadata?.tags ?? [],
author: mod.metadata?.author ?? ''
})); ファイル名がそのままURLスラッグになる(例: 2026-05-08-wordpress-to-sveltekit.md → /blog/2026-05-08-wordpress-to-sveltekit)ので、ルーティング設定も不要です。CMSなしでタグフィルタリング付きのブログが完成しました。
Tailwind CSS v4 でデザインシステムを作る
Tailwind CSS v4 では @theme ディレクティブでデザイントークンを定義できます。
@theme {
--font-sans: 'Inter', 'Noto Sans JP', sans-serif;
--color-brand: #0F6E56;
--color-brand-light: #1D9E75;
--color-brand-dark: #085041;
--color-brand-50: #f0fdf6;
--color-brand-100: #dcfce9;
} ブランドカラーを brand として登録しておけば、bg-brand, text-brand-dark のようにTailwindのクラスとして使えます。v4では tailwind.config.js が不要になり、CSSファイルだけでテーマを完結できるのがすっきりしていて良いです。
スクロールアニメーション
セクションがビューポートに入ったときにふわっとフェードインするアニメーションを、Svelteの use: ディレクティブで実装しました。
export function inview(node: HTMLElement) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
node.classList.add('animate-fade-in-up');
observer.unobserve(node);
}
});
},
{ threshold: 0.3, rootMargin: '0px 0px -80px 0px' }
);
node.style.opacity = '0';
observer.observe(node);
return {
destroy() {
observer.disconnect();
}
};
} 使う側は <div use:inview> と書くだけ。Svelteのアクション機能のおかげで、アニメーションのロジックをコンポーネントから完全に分離できました。rootMargin を -80px にすることで、要素が少し画面内に入ってからアニメーションが始まるようにしています。外部ライブラリなしで実現できるのがSvelteのいいところです。
SEO・OGP対応
SEOまわりは専用の SEO.svelte コンポーネントにまとめています。各ページで <SEO title="..." description="..." /> と呼び出すだけで、og:title, og:description, og:image, Twitter Cardなどのメタタグが自動で出力されます。
ブログ記事ページでは og:type を article に切り替えるなど、ページ種別に応じた出し分けにも対応しています。こういった地味な部分を共通コンポーネント化しておくと、あとから記事やページが増えても手間がかかりません。
移行してみてどうだったか
- 記事の追加がMarkdownを書くだけ — エディタで書いてpushするだけ。レビューもGitHubのPRで完結する
- ビルドが速い — Bunのおかげで依存解決もビルドも一瞬
- プレビューが簡単 — VercelがPRごとにプレビューURLを作ってくれるので、本番反映前の確認が楽
- 保守コストが激減 — PHP・MySQL・WordPress本体のアップデートから解放された
唯一気になっていたのはお問い合わせフォームの移行でした。旧サイトはWordPressのプラグイン「Contact Form 7」を使っており、これが使えなくなることをどう解決するか検討が必要でした。結果的にFormspreeで代替でき、むしろPHPもプラグインも不要なシンプルな構成になったので、問題というより改善でした。
特に大きいのは「コンテンツの更新がGitのワークフローに乗った」ということ。記事のレビュー、修正、公開の流れが普段の開発と同じになったので、チーム全員が自然に関われるようになりました。
まとめ
5人のエンジニアチームにとって、保守コストの低さは大事な判断基準でした。SvelteKit + Markdown の構成は、コーポレートサイトの運用としてちょうどいいバランスだと感じています。
WordPressが悪いわけではないですが、エンジニアが中心のチームであれば「コードとMarkdownで全部管理する」ほうがしっくりきます。AI agentを日常的に使う今の状況では、CMSよりもエンジニアが素直に理解できる実装にしておいた方が、結果的にみんなハッピーになれると感じています。