masakihirokawa

Rork Lab サイト構築テンプレート — SKILL.md

```

masakihirokawa 0 Updated 5mo ago

Resources

8
GitHub

Install

npx skillscat add masakihirokawa/rorklab-net

Install via the SkillsCat registry.

About this skill

We need to produce a 2-3 sentence plain-text summary in English, objective, factual, no marketing language, no superlatives, no calls to action. Must be at most 60 words. No quotes, no markdown formatting, no bullet points, no headings. Just plain text. We need to explain what the skill does, what problem it solves, when to use it. The skill is a site construction template for rorklab.net, summarizing architecture, content pipeline, etc. It provides a template for building knowledge base sites with Next.js, MDX, i18n, etc.

SKILL.md

Rork Lab サイト構築テンプレート — SKILL.md

このスキルファイルは rorklab.net の構築手順・アーキテクチャ・運用方法をまとめたものです。
同じ雛形で新しいナレッジベースサイトを立ち上げる際のリファレンスとして使えます。


アーキテクチャ概要

レイヤー 技術
フレームワーク Next.js 16 (App Router, Turbopack)
言語 TypeScript
スタイル Tailwind CSS 4 + CSS Variables (ダーク/ライト対応)
i18n next-intl v4(ja / en)
コンテンツ MDX → ビルド時にJSONへプリコンパイル
ホスティング Cloudflare Pages(OpenNext アダプター)
決済 Stripe Checkout Sessions(サブスクリプション)
分析 Google Analytics (gtag.js)
OGP 静的 PNG(public/og/rorklab-og.png、1200×1200px)— 全ページ共通
検索 クライアントサイド検索(タグ・タイトル・説明文のスコアベースランキング)
RSS /feed.xml (ja), /en/feed.xml (en) — 最新50件
サイトマップ 動的生成(hreflang付き、記事・ブログ・静的ページ)

サイト規模(2026-03-21 現在)

  • 記事: JA 153本 / EN 130本 = 283エントリ(うちプレミアム JA 38本 / EN 27本)
  • ブログ: JA 9本 / EN 9本 = 18エントリ
  • カテゴリ: rork-dev (88), rork-basics (26), rork-business (19), rork-ai (16), app-dev (4)

ディレクトリ構成

├── content/
│   ├── articles/
│   │   ├── ja/{category}/{slug}.mdx    # 日本語記事
│   │   └── en/{category}/{slug}.mdx    # 英語記事
│   └── blog/
│       ├── ja/{slug}.mdx
│       └── en/{slug}.mdx
├── public/
│   ├── icon.svg, icon-192.png, icon-512.png, apple-touch-icon.png
│   ├── favicon-16.png, favicon-32.png, favicon-48.png
│   ├── robots.txt                      # Sitemap + RSS 参照、AI クローラー許可
│   └── llms.txt                        # LLM アクセス情報
├── scripts/
│   └── generate-content.mjs           # MDX → JSON プリコンパイル
├── src/
│   ├── app/
│   │   ├── layout.tsx                  # ルートレイアウト(メタデータ基盤)
│   │   ├── globals.css                 # テーマ変数 + 記事スタイル
│   │   ├── sitemap.ts                  # 動的サイトマップ(hreflang付き)
│   │   ├── not-found.tsx
│   │   ├── feed.xml/route.ts           # RSS フィード(日本語)
│   │   ├── en/feed.xml/route.ts        # RSS フィード(英語)
│   │   ├── [locale]/
│   │   │   ├── layout.tsx              # ロケール別レイアウト(GA, フォント, テーマ, RSS link)
│   │   │   ├── page.tsx                # ホームページ(JSON-LD WebSite schema)
│   │   │   ├── HomeClient.tsx          # ホームページUI(クライアント)— 「すべての記事 (N) →」で総記事数を表示
│   │   │   ├── opengraph-image.tsx     # OGP fallback(実質未使用)
│   │   │   ├── articles/
│   │   │   │   ├── page.tsx            # 記事一覧(ページネーション + カテゴリフィルター)
│   │   │   │   └── [category]/
│   │   │   │       ├── page.tsx        # カテゴリ別一覧
│   │   │   │       └── [slug]/
│   │   │   │           └── page.tsx    # 記事詳細(TOC, シェア, 関連記事)
│   │   │   ├── blog/
│   │   │   │   ├── page.tsx
│   │   │   │   └── [slug]/
│   │   │   │       └── page.tsx
│   │   │   ├── guides/page.tsx
│   │   │   ├── support/page.tsx        # サポートページ(Ko-fi, PayPal, Wise, Revolut)
│   │   │   ├── privacy/page.tsx
│   │   │   ├── membership/page.tsx     # メンバーシップページ(プラン一覧 + プレミアム記事一覧)
│   │   │   ├── terms/page.tsx
│   │   │   └── tokusho/page.tsx
│   │   └── api/
│   │       ├── checkout/route.ts       # Stripe Checkout Session 作成(Pro: subscription / Premium: payment)
│   │       ├── customer-portal/route.ts # Stripe Customer Portal
│   │       ├── verify-session/route.ts  # Stripe セッション検証 → KV 保存 → Cookie 発行
│   │       ├── webhook/route.ts         # Stripe Webhook 受信 → KV 書き込み
│   │       ├── restore-access/route.ts  # Cookie 消失時のアクセス復元
│   │       └── search-data/route.ts    # 検索用 JSON API(タグ・レベル含む)
│   ├── components/
│   │   ├── layout/
│   │   │   ├── Header.tsx              # 固定ヘッダー(検索, テーマ, 言語, ♥サポート, メンバーシップリンク)
│   │   │   ├── Footer.tsx
│   │   │   ├── LocaleSwitcher.tsx      # 言語切り替え
│   │   │   ├── ThemeProvider.tsx        # ダーク/ライトモード
│   │   │   └── ThemeToggle.tsx
│   │   ├── mdx/
│   │   │   ├── Callout.tsx             # info/warning/tip/danger
│   │   │   └── mdx-components.tsx
│   │   └── ui/
│   │       ├── ArticlePagination.tsx   # ページネーション(← 1 2 3 ... →)
│   │       ├── BookRecommendation.tsx  # Amazon アソシエイト書籍
│   │       ├── GrainOverlay.tsx        # フィルムグレインエフェクト
│   │       ├── LevelBadge.tsx          # 初級/中級/上級バッジ
│   │       ├── MembershipCTA.tsx       # 無料記事末尾に自動表示するメンバーシップ誘導CTA
│   │       ├── NewsTicker.tsx          # 画面上部のスクロールニュース
│   │       ├── PremiumPaywall.tsx      # 有料記事のペイウォール(Pro/Premiumボタン)
│   │       ├── RelatedArticles.tsx     # 関連記事(スコアベース: カテゴリ+3, タグ+2)
│   │       ├── ScrollToTop.tsx         # フローティング「トップへ戻る」ボタン
│   │       ├── SearchModal.tsx         # 検索モーダル(タグ検索, キーボード操作, ハイライト)
│   │       ├── ShareButtons.tsx        # X / はてブ / LINE シェア
│   │       └── TableOfContents.tsx     # 目次自動生成(h2/h3, IntersectionObserver)
│   ├── generated/
│   │   ├── articles.json               # プリコンパイル済み記事データ
│   │   └── blog.json                   # プリコンパイル済みブログデータ
│   ├── i18n/
│   │   ├── config.ts, routing.ts, request.ts, navigation.ts
│   │   └── messages/{ja,en}.json       # UIテキスト翻訳
│   ├── lib/
│   │   └── content.ts                  # 記事・ブログ取得API
│   └── middleware.ts                   # next-intl ミドルウェア(※ OpenNext が proxy 未対応のため維持)
├── wrangler.toml                       # Cloudflare Workers 設定
├── open-next.config.ts                 # OpenNext アダプター設定
├── SKILL.md                            # ← このファイル
├── CONTENT_LOG.md                      # 全記事・ブログの管理ログ
├── REMAINING_TASKS.md                  # 残タスクリスト
└── package.json

新規サイト立ち上げ手順

1. プロジェクト初期化

npx create-next-app@latest {site-name} --typescript --tailwind --app --src-dir
cd {site-name}
npm install next-intl@^4 gray-matter unified remark-parse remark-rehype rehype-raw rehype-stringify stripe
npm install -D rehype-pretty-code shiki @opennextjs/cloudflare wrangler

2. i18n セットアップ(next-intl v4)

  • src/i18n/routing.ts — ロケール定義(['ja', 'en'], defaultLocale: 'ja'
  • src/i18n/request.ts — リクエストごとのロケール解決
  • src/middleware.ts — next-intl のミドルウェア設定
  • src/i18n/messages/{ja,en}.json — UI翻訳テキスト
  • src/app/[locale]/layout.tsx — ロケールに応じた <html lang>

3. コンテンツシステム

MDX 記事のフロントマター:

---
title: "記事タイトル"
slug: "url-slug"
category: "category-name"
level: "beginner|beginner-intermediate|intermediate|intermediate-advanced|advanced"
date: "YYYY-MM-DD"
updated: "YYYY-MM-DDTHH:MM"   # ソート優先度制御(任意)
author: "サイト名"
description: "メタディスクリプション"
tags: ["tag1", "tag2"]
premium: false  # 有料記事の場合 true
---

ソートルール: updated フィールドがあればそれを優先、なければ date。同日の記事は updated のタイムスタンプで順序を制御する。

プリコンパイル: scripts/generate-content.mjs で MDX を HTML に変換し src/generated/articles.json に出力。npm run generate で手動実行(注意: package.jsonprebuildgenerate にリネームすること。prebuild のままだと npm run build のたびに自動実行されて Cloudflare Pages で OOM が発生する)。

カテゴリ定義: src/lib/content.tsCATEGORIES 配列でアイコン・カラーを設定。

4. テーマシステム(ダーク/ライト)

  • CSS Variables で全色をテーマ対応(globals.css
  • ThemeProvider + ThemeToggle で localStorage ベースの切り替え
  • FOUC 防止のブロッキングスクリプトを <head> に挿入

5. OGP 画像

public/og/rorklab-og.png(1200×1200px 正方形)が全ページの OGP 画像として設定されている(src/app/layout.tsxopenGraph.images に直接 URL を指定)。LINE・Stripe Checkout のサムネイルにも正方形が最適。画像を更新する場合は _material/OGP images/ のデザインを編集してから public/og/rorklab-og.png に上書きコピーし、push する。

※ 記事・ブログ個別の OGP 画像は削除済み。全ページが共通画像を継承する。

6. SEO 設定

  • src/app/layout.tsx — ベースメタデータ(keywords, robots, authors)
  • src/app/[locale]/page.tsx — ロケール別メタデータ + JSON-LD (WebSite + FAQPage)
  • 記事ページ — JSON-LD (Article + BreadcrumbList) + hreflang alternates
  • src/app/sitemap.ts — 動的サイトマップ(hreflang, 記事実日付, ブログ含む)
  • public/robots.txt — Allow: / + Sitemap + RSS 参照 + AI クローラー許可
  • RSS フィード — /feed.xml (ja), /en/feed.xml (en)
  • <head> に RSS alternate link

7. Cloudflare Pages デプロイ

# ビルド
npx opennextjs-cloudflare build

# デプロイ(GitHub連携推奨)
git push origin main  # Cloudflare Pages が自動デプロイ

# 手動デプロイ(環境変数 CLOUDFLARE_API_TOKEN が必要)
CLOUDFLARE_API_TOKEN=xxx npx wrangler pages deploy .open-next/assets --project-name={project-name}

Cloudflare Pages 設定:

  • Build command: npx opennextjs-cloudflare build
  • Build output directory: .open-next/assets
  • 環境変数: STRIPE_SECRET_KEY, STRIPE_PRICE_ID(必要に応じて)

⚠️ 重要 — generate-content.mjs の OOM 対策: unified() プロセッサをモジュール先頭で1回だけ作成し .freeze() を呼ぶこと。記事ごとに unified() を新規作成すると shiki WASM が毎回初期化されて Cloudflare Pages ビルドが OOM で失敗する。

8. Stripe 統合

  • src/app/api/checkout/route.ts — Checkout Session 作成(subscription mode)
  • src/app/api/customer-portal/route.ts — Customer Portal リダイレクト
  • src/app/api/verify-session/route.ts — Stripe セッション検証 → Cloudflare KV 保存 → Cookie 発行
  • src/app/api/webhook/route.ts — Stripe Webhook 受信 → KV 書き込み
  • src/app/api/restore-access/route.ts — Cookie 消失時にメールアドレスで再認証
  • PremiumPaywall.tsx — 有料記事のペイウォール UI(Pro/Premium 両ボタン)
  • MembershipCTA.tsx — 無料記事末尾に自動表示(非プレミアム会員にのみ表示)
  • src/app/[locale]/membership/page.tsx — メンバーシップページ(プラン比較 + premium記事一覧)
  • Cloudflare KV PREMIUM_ACCESS — メール→Cookie トークンの保存(wrangler.toml にバインディング設定済み)
  • public/stripe.png — Stripe 商品サムネイル(1200×1200px 正方形)

9. Google Analytics

src/app/[locale]/layout.tsx<head> に gtag.js を配置:

<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX" />
<script dangerouslySetInnerHTML={{ __html: `window.dataLayer=window.dataLayer||[];function gtag(){dataLayer.push(arguments);}gtag('js',new Date());gtag('config','G-XXXXXXXXXX');` }} />

日常運用

記事の追加

  1. content/articles/{locale}/{category}/{slug}.mdx を作成(ja + en)
  2. node scripts/generate-content.mjs で JSON 再生成
  3. npx opennextjs-cloudflare build でビルド
  4. git push origin main でデプロイ

ニュースティッカーの更新

src/components/ui/NewsTicker.tsxNEWS_ITEMS オブジェクトを編集。

自動更新(スケジュールタスク)

claudelab-daily-content タスクが毎日 9:00 / 12:00 / 15:00 / 21:00 に実行:

  • _reference_keywords の優先キーワードから記事テーマを選択
  • _reference_urls の参考URLから最新情報を取得
  • 2-4 本の記事を日英で自動生成(2000-4000語、FAQ付き)
  • updated フィールドを付与してソート順を制御
  • ニュースティッカーを最新情報に更新
  • ビルド → Git push → デプロイ確認

新サイト作成時のカスタマイズポイント

新しいナレッジベース(例: antigravitylab.net)を作る場合:

  1. CATEGORIES を変更 — サイトのテーマに合わせたカテゴリ定義(src/lib/content.ts
  2. カラーパレットglobals.css の CSS Variables を差し替え
  3. フォント — layout.tsx の Google Fonts URL を変更
  4. OGP デザイン_material/OGP images/ のデザイン画像を public/og/rorklab-og.png として配置(1200×1200px)
  5. favicon — SVG → Pillow 等で全サイズを生成(16, 32, 48, 192, 512, apple-touch-icon)
  6. i18n メッセージsrc/i18n/messages/ のUI翻訳テキスト
  7. BookRecommendation — Amazon Associates のストアID変更
  8. Stripe — 新しい Product / Price を作成
  9. Google Analytics — 新しいプロパティの測定ID
  10. RSS フィードfeed.xml/route.ts のサイト名・説明文を変更
  11. robots.txt — Sitemap URL を新ドメインに
  12. wrangler.toml — プロジェクト名を変更
  13. スケジュールタスク — 新サイト用のキーワードリスト・参考URLに差し替え

コア構造(App Router, MDX システム, コンテンツAPI, 検索, ページネーション, SEO 基盤, RSS, サイトマップ, Cloudflare デプロイ)はそのまま再利用可能です。


技術スタック詳細

パッケージ バージョン 用途
next 16.1.6 フレームワーク
react / react-dom 19.2.3 UI ライブラリ
next-intl ^4.8.3 i18n
gray-matter ^4.0.3 MDX フロントマター解析
unified + remark + rehype ^11 MDX → HTML 変換
rehype-pretty-code + shiki コードハイライト
stripe ^20.4.1 決済
@opennextjs/cloudflare ^1.17.1 Cloudflare Pages アダプター
tailwindcss ^4 スタイル
wrangler ^4.71.0 Cloudflare CLI

⚠ 既知の問題と対応(2026-03-13)

1. MDX 内ハードコードの関連記事リンクによる架空記事表示

症状: 記事ページに「関連記事」が2重に表示される。上側の関連記事がリンク切れ(404)になる。

原因: 一部の MDX ファイルに ## 関連記事 / ## Related Articles セクションがハードコードされており、
動的な RelatedArticles.tsx コンポーネントとは別に表示されていた。
さらにそのリンクのURLが /articles/ja/category/slug(正しくは /articles/category/slug)や
/articles/en/category/slug(正しくは /en/articles/category/slug)という
間違ったロケールプレフィックス形式だった。

正しいURL形式(next-intl localePrefix: "as-needed" の場合):

  • 日本語(デフォルトロケール): /articles/{category}/{slug}
  • 英語: /en/articles/{category}/{slug}

対応:

  1. MDX ファイル内のハードコード ## 関連記事 セクションを全削除(RelatedArticles.tsx が動的に処理するため不要)
  2. MDX 内に残るインラインリンクのロケールプレフィックスを修正
  3. scripts/generate-content.mjs にバリデーションを追加:
    コンパイル後 HTML に href="/articles/ja/ または href="/articles/en/ が含まれる場合、
    コンソールに ⚠ WARNING を出力するようにした(誤ったリンクの早期検出のため)。

2. Tailwind v4 Preflight によるリストスタイル消去

症状: 記事本文の <ul> / <ol> リストに「・」や番号が表示されない。

原因: @import "tailwindcss" (Tailwind v4)の Preflight CSS が list-style: none を全 ul/ol に適用する。

対応: globals.css.article-content ul/ollist-style-typeli::marker スタイルを明示指定:

.article-content ul { list-style-type: disc; }
.article-content ul ul { list-style-type: circle; }
.article-content ol { list-style-type: decimal; }
.article-content li::marker { color: var(--text-secondary); }

3. パンくずリストのロケール未対応

症状: 日本語ページの記事パンくずリストに「Articles」と英語表示になる。

対応: src/app/[locale]/articles/[category]/[slug]/page.tsx のパンくずリストを修正:

{locale === "ja" ? "記事一覧" : "Articles"}