ランディングページビルダー — CAEパターン
概要説明
このスキルは、ユーザーから提供された情報に基づいて、高品質なランディングページ(CAE Việt NamのEECパターンに準拠)を作成します。全プロセスはAntigravityによって実行され、ユーザーは質問に答えるだけで済みます。
最終成果物:
index.html+styles.css— アニメーション付き、完全レスポンシブなランディングページgoogle-apps-script.js— フォームデータを受信し、Google Sheetsに記録し、通知メールを送信するバックエンド- Cloudflare Pagesへのデプロイ手順(ステップバイステップ)
EECページアーキテクチャ(標準設計図)
1. NAV — スティッキー、スクロールでブラー効果、ロゴ+リンク+CTAボタン
2. HERO — フルスクリーン、赤のグラデーション、パーティクル、見出し+サブテキスト+2つのボタン
3. STATS BAR — 4つの数値ハイライト、アニメーションカウンター
4. WHY US — 6つの機能カード(ホバーエフェクト付き、3列)
5. INSTRUCTOR(オプション) — YouTube Short埋め込み+資格情報
6. COURSES — 3つのコース、中央のカードがハイライト、プライスボックス
7. TIMELINE — N週間のロードマップ(グリッド形式)
8. TESTIMONIALS — 3つのレビューカード(星評価付き)
9. FAQ — アコーディオン(展開/折りたたみ)
10. CTA + FORM — グラデーションセクション、登録フォーム→ Google Sheets + メール
11. FOOTER — 3列:ブランド、コース、お問い合わせ
プロセス(順序に従って実行すること)
PHASE 0: スキャンとコンテキスト(常に最初に実行)
質問を始める前に、環境をチェックし、以前のコンテキストを探します:
-
Mem0が利用可能か確認:
search_memoryを次のクエリで呼び出してみる:"landing page preference màu font domain"- ツールが存在しない/エラーを報告する場合 → 完全にスキップし、すぐにPhase 1に進み、ユーザーにエラーを通知しない
- ツールが存在し、見つかった場合 → 次のように報告:「以前、[色X / フォントY / ドメインZ]を使用されていたのを覚えています。今回はそのまま使用しますか、それとも変更しますか?」
- ツールが存在するが見つからなかった場合 → 通常通りPhase 1に進む
-
新しいコンテキストを保存(Mem0が利用可能な場合) — ユーザーが7つのグループすべてを確認した後、
add_memoryを呼び出す:ランディングページ [キャンペーン名]: 色=[hex], フォント=Be Vietnam Pro, ドメイン=[domain], メール=[email], シート=[sheet link]- Mem0がない場合 → このステップをスキップ、結果に影響なし
設計原則: Mem0は追加機能(プログレッシブエンハンスメント)であり、必須の依存関係ではありません。スキルはMem0がない環境でも完全に正常に動作する必要があります。
PHASE 1: 情報収集(グループごとに質問、回答を待つ)
⚠️ 重要: グループごとに個別に質問します。一度にすべてを質問しないでください。ユーザーが1つのグループに回答した後に、次のグループについて質問します。
グループA — ブランドと色
質問:
ランディングページを作成するために、いくつかの基本情報が必要です。
**グループ1:ブランド**
1. ブランド名/センター名は何ですか?(例:CAE Việt Nam、ABCセンター...)
2. ロゴのPNG画像URLは?(または「なし」と言ってテキストで代用)
3. 使用したいメインカラーは?
- 🔴 CAEレッド(EECページと同様) — #8F1A1A + ゴールド #FEC46F
- 🔵 プロフェッショナルネイビー — #1A3A5C + シアン #00C9FF
- 🟢 フレッシュグリーン — #0D6E3F + ゴールド #FFD700
- ✏️ 他の色を入力します(16進数コードを提供)
4. 予定ドメインは?(例:uudai.tentruongtoi.com または「未定」)
グループB — ヒーローコンテンツ
グループAの回答後、質問:
**グループ2:ヘッダー部(Hero Section)**
1. 表示したいメイン見出しは?(例:「学術中国語100%奨学金」)
2. 見出しの上の小さなキャッチフレーズは?(例:「トライアルプログラム — 限定数」)
3. 見出しの下の短い説明文(1-2文)は?
4. メインCTAボタンのテキストは?(例:「📝 今すぐ登録」、「🎓 奨学金を受け取る」)
5. 表示したい4つの統計数値はありますか?(例:3クラス、100%無料、8週間、15名/クラス)
グループC — 強みとコース
グループBの回答後、質問:
**グループ3:強みと製品**
1. あなたの強みを3〜6つリストアップしてください(各強み:絵文字アイコン、短いタイトル、1〜2文の説明)
例:🎓 ライセンスプログラム | 国際パートナーからライセンスを購入...
2. 1〜3つのコース/サービスパッケージをリストアップしてください(各パッケージ):
- コース名
- 小さなバッジ(例:「オンライン · 8週間」、「オフライン · ハノイ」)
- 3〜4つの主な機能
- 元の価格(取り消し線)
- 割引価格 / 「100%無料」 / 割引率
- メモ(例:「15名限定」)
- どのコースが「最もおすすめ」か?
グループD — ロードマップと講師
グループCの回答後、質問:
**グループ4:追加コンテンツ**
1. 週ごとの学習ロードマップはありますか?(ある場合、N週間のトピック名をリストアップ)
2. 紹介したい講師/専門家はいますか?
- 名前、役職
- YouTube動画リンク(オプション)
- 3つの顕著な資格/実績
3. 3つのお客様の声はありますか?
(イニシャル、フルネーム、役割、引用内容)
グループE — FAQとお問い合わせ
グループDの回答後、質問:
**グループ5:FAQとお問い合わせ**
1. よくある質問とその回答を5〜7件リストアップしてください。
2. お問い合わせ情報:
- オフィス住所
- ホットライン/Zalo
- メール
- FacebookページURL
3. フッター著作権:"© 2025 [組織名]"
グループF — フォームとバックエンド
グループEの回答後、質問:
**グループ6:登録フォームとデータ接続**
1. フォームで収集するフィールドは?
- [x] 氏名(必須)
- [x] 電話番号(必須)
- [ ] メール
- [ ] 住所/都道府県
- [ ] コース選択チェックボックス
- [ ] メモ用テキストエリア
- [ ] その他のフィールド?
2. 登録があった場合に通知を受け取るメールアドレスは?
(例:[email protected])
3. 使用したい通知メールの件名は?
(例:「🔔 ランディングページから新しい登録がありました!」)
4. 登録データを保存したいGoogle Sheetのリンクをお送りください。
(上記のフォームフィールドに基づいてスクリプトを作成し、そのシートで実行する手順を説明します)
⚠️ ユーザーがシートリンクを送信したら → 停止して、すぐにPHASE 2を実行します。
グループG — Cloudflareへのデプロイ
グループFの回答後、質問:
**グループ7:デプロイ情報(最終ステップ)**
1. Cloudflare Account IDは?
(dash.cloudflare.comにアクセス → アカウントを選択 → URLを確認:cloudflare.com/ACCOUNT_ID)
2. Cloudflare API Tokenは?
(My Profile → API Tokens → Create Token → テンプレート "Edit Cloudflare Pages" を使用)
3. Cloudflare Pagesのプロジェクト名は?(小文字、ハイフン区切り、例:my-landing-2025)
4. 割り当てるカスタムドメインは?(例:uudai.domain.com または「不要」)
PHASE 1.5 — VISION 10-STAR ⭐(生成前に必須)
"何百万人もの人のためにデザインしないでください。一人の人がこれまでに経験した中で最も驚くべき体験をデザインしてください。" — Garry Tan
立ち止まり、コードを1行も書く前にこれら4つの質問に答えてください:
-
見出しは「釘」のように刺さっていますか?
- 3秒で見出しを読んだとき、顧客はこれが自分向けだとすぐにわかりますか?
- 十分に強力でない場合 → このスキルのコピーフォーミュラを適用して書き直す
-
このページに「忘れられない何か」はありますか?
- 印象的な数字?大胆な約束?予想外のメリット?
- 例:「100%無料学習 — 15名限定」→ 明確、希少性、即行動
-
このランディングページが犯しうる致命的な弱点は何ですか?
- フォームのフィールドが多すぎる?ヒーローにソーシャルプルーフがない?CTAが弱い?
- 特定し、デザインで修正する
-
もしスティーブ・ジョブズがこのページをレビューしたら、何を削るでしょうか?
- 唯一の目標である登録に貢献しないものはすべて排除する
出力: 続行する前に、応答にVisionノートを記録します:
🎯 VISION 10-STAR:
- ユニークな強み:[..]
- 決定版見出し:[..]
- 忘れられない何か:[..]
- 削除したもの:[..]
FOUNDER CHALLENGE RULE(Vision Gateで適用)
"CEOの役割を演じる — 使命は凡庸さに挑戦することであり、それを認めることではない。"
ユーザーが弱い見出し(一般的で、対象が不明確で、具体的なメリットがない)を提供した場合、すぐに受け入れてはいけません。代わりに:
⚡ Founder Mode:
見出し「[ユーザーの見出し]」はまだ一般的です。より強力な3つの代替案を提案します:
[A] [具体的なメリット] — [数字/期間] — [明確な対象]
例:「学術中国語を100%無料で学ぶ — 15名限定」
[B] [質問のフック] + [予想外の結果]
例:「あなたのお子様は8週間で中国語を学び、留学生よりも自信がつきますか?」
[C] [大胆な約束] + [証拠]
例:「4回のレッスンで進歩がなければ、100%返金、理由は問いません」
どの方向性を選びますか、それとも別のアイデアで書き直しましょうか?
いつ挑戦するか: 見出しに次の3つの要素のうち少なくとも2つがない場合:(1) 明確な対象、(2) 具体的なメリット、(3) 数字/約束。
PHASE 2: Google Apps Scriptの作成(バックエンド)
トリガー: ユーザーがGoogle Sheetのリンクを送信した(グループF.4)。
ステップ1 — カスタマイズされたスクリプトを生成:
templates/google-apps-script.jsのテンプレートに基づき、ランディングページのフォームに合わせてカスタマイズされたスクリプトを作成します:
NOTIFICATION_EMAILにグループF.2のメールを入力EMAIL_SUBJECTにグループF.3の件名を入力CAMPAIGN_NAMEにグループA.1のキャンペーン名を入力- グループF.1のフォームフィールドに一致する正しい列数になるように
writeToSheet()関数を更新 - 選択されたフィールドに応じて適切な
e.parameterを解析するようにdoPost()関数を更新
ステップ2 — ユーザーがコピーするためのコードブロックを出力:
生成後、コード全体を1つのブロックで次のように表示します:
✅ Google Sheet用のスクリプトを作成しました!
こちらがシートにコピー&ペーストするためのコードです:
[CODE BLOCK]
📋 GOOGLE SHEETでの実行手順(5ステップ):
① リンクを送信したGoogle Sheetを開く → メニュー Extensions → Apps Script
(またはSheetリンクをクリックし、上部メニューからExtensionsを探す)
② エディタ内のデフォルトコードをすべて削除(Ctrl+A → Delete)
③ 上記で作成したコードをすべて貼り付け(Ctrl+V)
④ Ctrl+Sで保存 → プロジェクト名を「Landing Page Handler」に設定 → OK
⑤ スクリプトをデプロイ:
• 「Deploy」ボタン(右上)→ 「New deployment」
• 「Type」: 「Web app」を選択
• 「Execute as」: 「Me(あなたのメール)」を選択
• 「Who has access」: 「Anyone」を選択
• 「Deploy」をクリック → Googleが権限を要求 → 「Authorize access」をクリック → Googleアカウントを選択
• 表示された長いURL(形式:https://script.google.com/macros/s/.../exec)をコピー
📌 そのURLが取得できたら、ここに貼り付けて、ランディングページに組み込みます。
ステップ3 — PHASE 3に進む前に、ユーザーがSHEETS_ENDPOINT URLを確認するのを待ちます。
PHASE 3: ランディングページHTMLの生成
収集したすべての情報に基づいて、完全なindex.htmlファイルを作成します:
作成するファイルの構成:
- 単一の
index.htmlファイル(CSSは<style>タグ内にインライン、別ファイルに分割しない) - JavaScriptフォーム送信も
<script>タグ内にインライン - フレームワークに依存せず、バニラHTML/CSS/JSのみ
- 完全レスポンシブ(モバイルファースト)
- Be Vietnam Proフォントを使用(Google Fonts CDNからロード)— ベトナム語のアクセント記号を完全サポート、Inter/Montserratの代わり
EECパターンによるデザイン:
- グループA.3のカラースキーム
- 設計図のすべてのセクション
- ユーザーがインストラクターを提供しない場合 → そのセクションを削除
- お客様の声がない場合 → そのセクションを削除
- タイムラインがない場合 → そのセクションを削除
フォーム送信パターン(隠しiframeを使用すること — CORSを回避):
const SHEETS_ENDPOINT = 'URL_GAS_CUA_USER'; // ステップ2のURLを入力
const hiddenFrame = document.createElement('iframe');
hiddenFrame.name = 'hidden_iframe';
hiddenFrame.style.display = 'none';
document.body.appendChild(hiddenFrame);
// 送信時:隠しフォームを作成、GASエンドポイントにPOST、ターゲットを隠しiframeに
// fetch()は使用しない — ネイティブフォームPOSTはCORSブロックされない
localStorageへのバックアップ:
// 送信前に常にlocalStorageにバックアップ
const regs = JSON.parse(localStorage.getItem('lp_registrations') || '[]');
regs.push(data);
localStorage.setItem('lp_registrations', JSON.stringify(regs));
楽観的UIとGAS Resilience(必須):
// 1. 成功を即座に表示 — GAS応答を待たない
function submitForm(data) {
// 最初にlocalStorageにバックアップ
backupToLocal(data);
// すぐに成功メッセージを表示(楽観的)
showSuccessMessage();
// GASにバックグラウンド送信 — UIをブロックしない
submitToGAS(data).catch(() => {
// GASが失敗しても、localStorageにデータはある → サイレントリトライ
scheduleRetry(data);
});
}
// 2. GASが失敗した場合の自動リトライ
function scheduleRetry(data, attempt = 1) {
if (attempt > 3) return; // 3回試行後、諦める
setTimeout(() => {
submitToGAS(data).catch(() => scheduleRetry(data, attempt + 1));
}, attempt * 3000); // 3秒、6秒、9秒
}
// 3. GASタイムアウト10秒(ユーザーを無限に待たせない)
async function submitToGAS(data) {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 10000);
try {
// no-corsモードのfetchを使用(GASは応答本文を返さない)
await fetch(SHEETS_ENDPOINT, {
method: 'POST', mode: 'no-cors',
body: new URLSearchParams(data),
signal: controller.signal
});
} finally {
clearTimeout(timeout);
}
}
注:
fetch+URLSearchParamsでのno-corsモードは、隠しiframeの代替方法であり、CORSブロックされません。コンテキストに応じてどちらかを使用します。
PHASE 3.5 — BROWSER VERIFY(デプロイ前)
トリガー: index.htmlファイルが作成された。
ブラウザツールを使用して、ユーザーに確認を促す前に検証します:
- ブラウザでファイルを開く(またはローカルサーバー)
- レスポンシブチェック:
- モバイル375px:メニューが崩れていない、フォームが画面に収まっている、テキストがカットされていない
- デスクトップ1440px:広すぎない、レイアウトがバランス良い
- UXチェック:
- CTAボタンがファーストビューで見える(スクロール不要)
- フォーム送信:テストデータを入力 → コンソールでエラーがないか確認
- FAQアコーディオンの開閉が機能する
- スムーズスクロール、カクつかない
- ビジュアルチェック:
- 選択したパレットの色が正しい
- フォントが正しくロードされている(Be Vietnam Pro)
- プレースホルダーテキストが残っていない
- スクリーンショットとレポート: デスクトップとモバイルのスクリーンショットを撮り、ユーザーにプレビューとして送信
⚠️ PHASE 3.6 RefineとPHASE 4 Deployを実行する前に、このチェックリストをすべてパスする必要があります。
PHASE 3.6 — REFINE PASS(磨き上げ)
ユーザーがコンテンツOKを確認した後、1回の磨き上げを行います:
- マイクロアニメーション: 各セクションがスクロールインするときに
fade-inを追加(IntersectionObserver) - ホバーエフェクト: ホバー時にボタンをわずかに拡大(
transform: scale(1.03)) - CTAパルス: メインの登録ボタンに微妙なパルスアニメーションを追加して注目を集める
- カウンターアニメーション: 統計数値(3クラス、500+名など)がビューポートに入ったときにカウントアップ
- フォームフィードバック: 送信時にローディング状態を表示、送信後に成功メッセージを表示
- ナビブラー: スクロールダウン時にナビゲーションに
backdrop-filter: blur()を適用 - アクセシビリティ: すべての画像に
altテキスト、<button>のaria-label、フォーカスアウトラインを確認 - パフォーマンス: 画像に
loading="lazy"、CSSにwill-change、フォームにinputmodeを確認
PHASE 4: Cloudflare Pagesへのデプロイ
前提条件: ユーザーがグループ7の情報(Cloudflare Account ID、API Token、プロジェクト名)を提供している。
ステップ1 — Wrangler設定ファイルを作成:
プロジェクトのルートにwrangler.tomlファイルを作成します:
name = "[グループ7.3のプロジェクト名]"
compatibility_date = "2025-01-01"
[env.production]
route = { pattern = "[グループ7.4のドメイン]", zone_id = "[ユーザーのゾーンID]" }
ステップ2 — Cloudflare API経由でデプロイ:
Cloudflare APIをプログラムで呼び出すか、wrangler CLIを使用します(利用可能な場合)。ユーザーがwranglerをインストールしている場合は、次のコマンドを提供します:
npx wrangler pages project create [プロジェクト名]
npx wrangler pages deploy . --project-name [プロジェクト名]
ステップ3 — デプロイ結果をユーザーに報告:
デプロイが成功したら:
✅ デプロイ完了!
🌐 公開URL:https://[プロジェクト名].pages.dev
🔗 カスタムドメイン:[ドメイン](設定されている場合)
📌 次のステップ:
- CloudflareダッシュボードでHTTPSが有効になっていることを確認
- カスタムドメインを設定した場合、DNSレコードが正しく伝搬していることを確認
- ランディングページのURLを共有して、トラフィックの受付を開始!
付録:テクニカルノート
Google Apps Scriptテンプレート(templates/google-apps-script.js)
// このスクリプトはランディングページフォームのデータを処理します
// 新しいスクリプトを作成するたびに以下の変数を更新してください
var SHEET_NAME = 'Sheet1';
var NOTIFICATION_EMAIL = '[email protected]'; // ← 更新
var EMAIL_SUBJECT = '🔔 新しいランディングページ登録がありました!'; // ← 更新
var CAMPAIGN_NAME = 'キャンペーン名'; // ← 更新
function doPost(e) {
try {
// 送信されたデータを解析
var data = {};
if (e && e.parameter) {
for (var key in e.parameter) {
data[key] = e.parameter[key];
}
}
// Google Sheetsに書き込み
writeToSheet(data);
// 通知メールを送信
sendNotification(data);
return ContentService.createTextOutput(
JSON.stringify({ result: 'success' })
).setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService.createTextOutput(
JSON.stringify({ result: 'error', message: err.toString() })
).setMimeType(ContentService.MimeType.JSON);
}
}
function writeToSheet(data) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName(SHEET_NAME);
// ヘッダーがまだない場合は追加
if (sheet.getLastRow() === 0) {
var headers = Object.keys(data);
sheet.getRange(1, 1, 1, headers.length).setValues([headers]);
}
// データ行を追加
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var row = [];
for (var i = 0; i < headers.length; i++) {
row.push(data[headers[i]] || '');
}
sheet.appendRow(row);
}
function sendNotification(data) {
var htmlBody = '<h2>新しいランディングページ登録</h2>' +
'<p><strong>キャンペーン:</strong> ' + CAMPAIGN_NAME + '</p>' +
'<table border="1" cellpadding="5">';
for (var key in data) {
htmlBody += '<tr><td><strong>' + key + '</strong></td><td>' + data[key] + '</td></tr>';
}
htmlBody += '</table>';
MailApp.sendEmail({
to: NOTIFICATION_EMAIL,
subject: EMAIL_SUBJECT,
htmlBody: htmlBody
});
}
コピーフォーミュラ(強力な見出し用)
強力な見出しは、次の4つの要素のうち少なくとも3つを含む必要があります:
- 対象者: 誰のためのものか?(「社会人向け」、「保護者向け」)
- メリット: 何を得られるのか?(「中国語をマスター」、「キャリアアップ」)
- 数字: どのくらいの期間/量か?(「8週間」、「15名限定」)
- 緊急性/希少性: なぜ今行動すべきか?(「限定枠」、「今週まで」)
例:
- 弱い:「中国語コース」
- 強い:「15名限定!社会人のための中国語コース — 8週間で基礎をマスター」
コンバージョン最適化のヒント
- CTAの配置: メインCTAはファーストビューに表示。2番目のCTAは証拠(お客様の声、統計)の後に配置。
- フォームの長さ: フィールド数が多いとコンバージョン率が低下。必須フィールドのみに制限。
- ソーシャルプルーフ: お客様の声と統計は、見出しまたはCTAの近くに配置。
- モバイル最適化: ボタンはタップしやすいサイズ(最小44px)、フォームは入力しやすい。
- A/Bテスト: 見出し、CTAテキスト、画像のバリエーションをテスト。