B2Bサービスサイト公開後に気づいた「発注されない理由」と7つの改善実装
Astro 5で構築したB2Bサービスサイトを公開後、問い合わせが来ない原因を分析。料金ページ・FAQ・ブログCTAなど7つの改善をゼロJSで実装した全工程を解説。
最終更新:
B2Bサービスサイト公開後に気づいた「発注されない理由」と7つの改善実装
この記事で得られること
この記事を読むと、B2Bサービスサイトの「問い合わせが来ない」問題を構造的に分析し、Astro 5で具体的な改善を実装する方法がわかります。
- サービスサイト公開後にチェックすべき「発注の壁」5つの観点
- 料金ページをTypeScriptデータ駆動 + JSON-LD構造化データで作る方法
<details>/<summary>でJS 0KBのFAQアコーディオンを実装するパターン- ブログ記事からB2Bサイトへの導線(CTA)をPure Astroコンポーネントで作る手法
あなたもこんな悩みを抱えていませんか?
- 「サービスサイトは作ったのに、問い合わせが全然来ない…」
- 「技術力には自信があるけど、サイトを見た人が何を思うかわからない…」
- 「料金を載せるべきか、載せないべきか迷っている…」
私もまさに同じ壁にぶつかっていました。
私のストーリー:「作って終わり」の罠
Before:完成したはずのサイト
前回の記事で書いた通り、Astro 5 + Tailwind CSS v4 でB2Bサービスサイトを構築しました。
12ページ、1.46秒ビルド、ゼロJSアーキテクチャ。技術的には満足のいく出来でした。
しかし、公開してしばらく経っても、問い合わせフォームからの連絡はゼロ。
最初は「まだ認知されていないだけだ」と思っていました。でも、ブログ記事やSNSからのアクセスはそれなりにある。サイトには来ているのに、問い合わせには至らない。
「何かがおかしい。」
そう思い、サイトを「発注する側」の目で見直してみることにしました。
転機:自分のサイトを「クライアントの目」で見た瞬間
視点を変えた瞬間、見えてきたものがありました。
想像してみてください。あなたがSNS自動化を外注したいフリーランスだとします。検索で私のサイトにたどり着きました。
- まずヒーローセクション。「あなたのビジネスを、技術で加速する。」——「ビジネス」という言葉に、なんとなく大企業向けの印象を受ける
- サービス一覧を見る。内容はわかるが、いくらかかるのかがどこにも書いていない
- 「導入の流れ」を見る。ヒアリング→設計→実装→テスト→リリース。でも具体的にどうやって進むの? Slackで連絡するの?対面?
- 問い合わせフォームを見る。カテゴリが3つしかない。「LP作ってほしいだけなんだけど、どれを選べばいいの?」
- 聞きたいことはあるけど、よくある質問のページがない。「NDAは?」「フリーランスなの?」——フォームに書くほどでもない疑問が解消されない
結論:「問い合わせる前に離脱する理由」が山ほどあった。
この気づきが、今回の改善の出発点でした。
After:7つの改善で「問い合わせやすいサイト」へ
分析した結果、以下の7つの改善を実施しました:
| # | 改善内容 | 解決する課題 |
|---|---|---|
| 1 | ヒーローコピー変更 | 「企業向け感」の排除 |
| 2 | 料金ページ新設 (/pricing) | 価格不明による離脱 |
| 3 | FAQページ新設 (/faq) | 小さな疑問の未解消 |
| 4 | 問い合わせカテゴリ追加 | 個人・LP制作の選択肢不足 |
| 5 | 導入の流れ 詳細化 | プロセスの不透明さ |
| 6 | ナビゲーション更新 | 新ページへの導線 |
| 7 | ブログCTAコンポーネント | ブログ→B2Bサイトの導線不在 |
すべてAstro 5のゼロJSアーキテクチャを維持したまま、追加のJavaScriptなしで実装しました。
なぜこのアプローチを選んだのか
「発注の壁」を5つに分類する
問い合わせが来ない原因を分析するフレームワークとして、私は 「発注の壁」 という考え方を使いました。
| 壁 | 具体的な不安 | 解消する情報 |
|---|---|---|
| 信頼の壁 | この人に任せて大丈夫? | 資格・実績・プロセスの透明性 |
| 価格の壁 | いくらかかるかわからない | 料金ページ・目安価格 |
| 理解の壁 | 何をしてくれるのかわからない | サービス詳細・FAQ |
| 心理の壁 | 問い合わせるのが面倒・怖い | フォームの簡略化・個人歓迎の明示 |
| 導線の壁 | そもそもサイトにたどり着けない | ブログからの誘導・SEO |
初期のサイトは「信頼の壁」(資格バッジ、実績6件)はクリアしていたが、それ以外の4つの壁がほぼ放置されていたことに気づきました。
技術力のアピールに注力するあまり、「発注する側が何を知りたいか」という視点が抜け落ちていたのです。
最初の設計判断:「価格を載せない」は本当に正しかったのか
前回の記事で、教訓の一つとして**「価格表 → 入れない(問い合わせベースのほうがB2Bに合う)」**と書きました。
これは大企業相手のB2Bなら正しい判断です。要件によって価格が大きく変わるため、目安価格が逆にミスリードになる。
しかし、私のターゲットにはフリーランスや中小企業も含まれる。彼らにとって「価格が見えない」は「高そうだから問い合わせるのをやめよう」と同義です。
「問い合わせのハードルを下げる」のは「安く見せる」ことではない。「見通しを与える」ことだ。
この発想の転換が、料金ページ新設の決め手になりました。
もう一つの気づき:ブログとサービスサイトが断絶していた
技術ブログには継続的にアクセスがある。しかし、ブログの読者がサービスサイトに遷移する導線がゼロでした。
記事を読んで「この人に頼みたい」と思ったとしても、フッターのリンクすら目に入らない。ブログ読者は潜在顧客なのに、その接点を活かしていなかった。
ブログ記事の文脈に合わせたCTA(Call To Action)バナーを挿入すれば、自然な形でサービスサイトへ誘導できる。しかし、ブログはAstroで構築されており、Reactは入っていない。Pure Astroコンポーネントで、JSゼロのCTAを作る必要がありました。
具体的な実装方法
全体の変更マップ
secure-auto-lab-portfolio/ ← B2Bサービスサイト
├── src/
│ ├── components/
│ │ ├── Hero.astro ← コピー変更
│ │ ├── Header.astro ← ナビ6項目化
│ │ ├── Contact.astro ← カテゴリ追加
│ │ └── Process.astro ← 詳細ステップ追加
│ ├── data/
│ │ └── pricing.ts ← NEW: 料金データ
│ └── pages/
│ ├── pricing.astro ← NEW: 料金ページ
│ ├── faq.astro ← NEW: FAQページ
│ └── contact.astro ← カテゴリ追加
article-publisher/blog/ ← 技術ブログ
├── src/
│ ├── components/
│ │ └── BlogCTA.astro ← NEW: B2B誘導CTA
│ └── layouts/
│ └── ArticleLayout.astro ← CTA統合
Step 1: 料金ページ — TypeScriptデータ駆動 + JSON-LD
料金データをTypeScriptファイルに分離し、ページとJSON-LD構造化データの両方から参照する設計にしました。
// src/data/pricing.ts
export interface PricingTier {
name: string;
price: string;
description: string;
duration: string;
}
export interface PricingCategory {
id: string;
title: string;
icon: string;
tiers: PricingTier[];
}
export const pricingCategories: PricingCategory[] = [
{
id: 'automation',
title: '業務自動化・AI統合',
icon: '⚡',
tiers: [
{
name: 'スポット(小規模)',
price: '10万円〜',
description: 'SNS自動投稿、スクレイピング、レポート自動生成',
duration: '1〜2週間',
},
// ...安い順に並べる
],
},
// 全4カテゴリ・14ティア
];
ポイント:安い順に並べる。 最初に見える価格が「10万円〜」と「80万円〜」では、訪問者の印象がまるで違います。ハードルを下げたいなら、最小価格を先に見せる。
料金ページではJSON-LD構造化データも出力しています:
---
// src/pages/pricing.astro
import { pricingCategories } from '../data/pricing';
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'ProfessionalService',
name: 'Secure Auto Lab',
hasOfferCatalog: {
'@type': 'OfferCatalog',
name: 'サービス料金',
itemListElement: pricingCategories.map((cat) => ({
'@type': 'OfferCatalog',
name: cat.title,
itemListElement: cat.tiers.map((tier) => ({
'@type': 'Offer',
name: `${cat.title} - ${tier.name}`,
description: tier.description,
})),
})),
},
};
---
<script type="application/ld+json" set:html={JSON.stringify(jsonLd)} />
ProfessionalService + OfferCatalog スキーマで、Google検索に料金情報が構造化されて表示される可能性があります。
カードのグリッドは、カテゴリごとのティア数に応じて動的に列数を変えています:
<div class={`grid gap-4 ${
cat.tiers.length === 2 ? 'md:grid-cols-2' :
cat.tiers.length === 4 ? 'md:grid-cols-2 lg:grid-cols-4' :
'md:grid-cols-3'
}`}>
Step 2: FAQページ — JS 0KBのアコーディオン
FAQページで最も意識したのは、JavaScriptを一切使わないこと。HTMLの <details> / <summary> 要素はブラウザネイティブのアコーディオン機能を持っているため、開閉のためにJSは不要です。
---
// src/pages/faq.astro
const faqs = [
{
question: '個人(フリーランス)ですか?',
answer: 'はい、個人事業主として活動しています。...',
},
// 全6問
];
---
{faqs.map((faq) => (
<details class="faq-item group">
<summary class="faq-summary">
<span>{faq.question}</span>
<span class="faq-icon">+</span>
</summary>
<div class="faq-answer" set:html={faq.answer} />
</details>
))}
+ アイコンの回転アニメーションもCSSだけで実現:
.faq-icon {
color: var(--color-gold);
transition: transform 0.2s;
}
.faq-item[open] .faq-icon {
transform: rotate(45deg);
}
/* ブラウザデフォルトの三角マーカーを消す */
.faq-summary::-webkit-details-marker {
display: none;
}
rotate(45deg) で + が × に変わる。CSSだけで実現できるUIパターンは、積極的に活用すべきです。
構造化データは FAQPage スキーマ:
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
// HTML タグを除去してテキストのみ
text: faq.answer.replace(/<[^>]*>/g, ''),
},
})),
};
FAQ回答にはHTML(例:料金ページへのリンク)を含む場合があるため、JSON-LD用にはHTMLタグを正規表現で除去しています。
Step 3: ブログCTA — React不要のPure Astroコンポーネント
ブログ(article-publisher/blog)には @astrojs/react が入っていません。CTAコンポーネントをReactで書くと、Reactランタイムの追加(約40KB gzip)が必要になります。
hover効果にJavaScriptは不要です。CSSの :hover で十分。
---
// blog/src/components/BlogCTA.astro
interface Props {
variant?: 'automation' | 'security' | 'appdev' | 'general';
}
const { variant = 'general' } = Astro.props;
const VARIANTS = {
automation: {
icon: '⚡',
accent: '#00d4aa',
accentDim: 'rgba(0,212,170,0.12)',
tagline: 'この自動化、あなたの業務にも',
headline: '業務自動化・AI統合を依頼しませんか?',
// ...
},
security: { /* ... */ },
appdev: { /* ... */ },
general: { /* ... */ },
} as const;
const v = VARIANTS[variant] || VARIANTS.general;
---
<div class="blog-cta"
style={`--cta-accent: ${v.accent}; --cta-accent-dim: ${v.accentDim};`}>
<!-- カード本体 -->
</div>
<style>
.cta-card:hover {
border-color: color-mix(in srgb, var(--cta-accent) 40%, transparent);
box-shadow: 0 0 30px color-mix(in srgb, var(--cta-accent) 8%, transparent);
}
</style>
設計のポイント:color-mix() 関数によるアクセント色の動的制御。
CSS カスタムプロパティ --cta-accent にバリアントごとの色を設定し、color-mix() で透明度を動的に計算しています。これにより、4バリアントのhover効果をCSS 1つで実現しています。
Step 4: 記事カテゴリ → CTAバリアントの自動マッピング
ブログ記事のカテゴリに応じて、表示するCTAバリアントを自動的に切り替えます。
---
// blog/src/layouts/ArticleLayout.astro
import BlogCTA from '../components/BlogCTA.astro';
const ctaVariantMap: Record<string, 'automation' | 'security' | 'appdev' | 'general'> = {
automation: 'automation',
ai: 'automation', // AI記事 → 自動化CTA
security: 'security',
'dev-tips': 'general',
projects: 'appdev', // プロジェクト記事 → アプリ開発CTA
web: 'appdev', // Web記事 → アプリ開発CTA
infrastructure: 'general',
};
const ctaVariant = ctaVariantMap[category] || 'general';
---
<!-- 記事本文の後に挿入 -->
<div class="my-12">
<BlogCTA variant={ctaVariant} />
</div>
自動化の記事を読んでいる人には「業務自動化・AI統合を依頼しませんか?」、セキュリティの記事を読んでいる人には「セキュアなシステム開発を依頼しませんか?」と表示される。読者の関心に合わせたCTAは、汎用CTAよりクリック率が高いのは言うまでもありません。
Step 5: 小さいけれど重要な3つの修正
1. ヒーローコピー変更(1行の修正、インパクトは大)
- あなたのビジネスを、技術で加速する。
+ あなたのアイデアを、技術で形にする。
「ビジネス」→「アイデア」で、個人やスタートアップを含む幅広い層に訴求。「加速する」→「形にする」で、ゼロからの開発にも対応する印象を与える。
2. お問い合わせカテゴリ追加
<option value="automation">業務自動化・AI統合</option>
<option value="secure-dev">セキュアなシステム開発</option>
<option value="app-dev">アプリ・サービス開発</option>
<option value="lp-personal">LP制作・個人向けの相談</option> <!-- NEW -->
<option value="other">その他</option>
「LP制作・個人向けの相談」を追加。個人でも気軽に選べる選択肢があるだけで、心理的ハードルが下がる。
3. 導入の流れ 詳細化
各ステップに具体的な手段と成果物を追加:
const steps = [
{
num: '01', title: 'ヒアリング',
desc: '課題・要件を整理し、ゴールを明確にします。',
details: [
'Slack・メール・Google Meetで対応',
'NDA締結後のヒアリングも可能',
'1〜2回のMTGで要件を整理',
],
},
// ...
];
「ヒアリング」とだけ書かれるより、「Slackで連絡できるんだ」「NDAにも対応してくれるんだ」とわかるほうが安心する。抽象的なプロセスを具体化するだけで、信頼感は大きく変わります。
実践Tips・Astroでよく使うパターン集
Tips: <details>/<summary> でJS不要のアコーディオンを作る
AstroやNext.jsでFAQページを作るとき、わざわざReactの useState でアコーディオンを実装していませんか?
HTMLネイティブの <details>/<summary> を使えば、JSゼロで同じことが実現できます。
<details class="faq-item">
<summary class="faq-summary">
<span>質問テキスト</span>
<span class="faq-icon">+</span>
</summary>
<div class="faq-answer">回答テキスト</div>
</details>
/* ブラウザデフォルトの三角マーカーを消す */
.faq-summary {
list-style: none;
cursor: pointer;
display: flex;
justify-content: space-between;
}
.faq-summary::-webkit-details-marker {
display: none;
}
/* 開閉アイコンのアニメーション */
.faq-icon {
transition: transform 0.2s;
}
.faq-item[open] .faq-icon {
transform: rotate(45deg); /* + → × に変化 */
}
ポイント:
list-style: noneと::-webkit-details-markerの両方を指定(ブラウザ互換性)[open]属性セレクタでスタイルを切り替え(JSイベント不要)rotate(45deg)で+が×に見える(フォントアイコン不要)
Tips: Astroで型安全な料金データを管理する
料金のようなビジネスデータは、TypeScriptファイルに分離すると管理しやすくなります。
// src/data/pricing.ts
export interface PricingTier {
name: string;
price: string;
description: string;
duration: string;
}
export interface PricingCategory {
id: string;
title: string;
icon: string;
tiers: PricingTier[];
}
export const pricingCategories: PricingCategory[] = [
{
id: 'automation',
title: '業務自動化・AI統合',
icon: '⚡',
tiers: [
{
name: 'スポット(小規模)',
price: '10万円〜',
description: 'SNS自動投稿、スクレイピング、レポート自動生成',
duration: '1〜2週間',
},
],
},
];
メリット:
- 料金変更時、データファイル1つを編集するだけ
- ページ本体とJSON-LD構造化データの両方から同じデータを参照
- TypeScriptの型チェックで、必須フィールドの漏れを防止
Tips: color-mix() でCSS変数から動的に透明度を生成する
CSSカスタムプロパティに色を設定し、hover効果で透明度を変えたい場合があります。従来は rgba() で別の変数を用意する必要がありましたが、color-mix() なら1つの変数から動的に生成できます。
:root {
--accent: #00d4aa;
}
.card:hover {
/* --accent の40%の不透明度 */
border-color: color-mix(in srgb, var(--accent) 40%, transparent);
/* --accent の8%の不透明度 */
box-shadow: 0 0 30px color-mix(in srgb, var(--accent) 8%, transparent);
}
color-mix(in srgb, 色 割合%, transparent) で、任意の色に透明度を適用できます。
対応ブラウザ: Chrome 111+, Firefox 113+, Safari 16.2+ (2024年時点でほぼ全ブラウザ対応)
Tips: JSON-LD構造化データでFAQをGoogle検索に表示する
Googleは FAQPage スキーマを認識すると、検索結果にFAQリッチリザルトを表示する場合があります。
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqs.map((faq) => ({
'@type': 'Question',
name: faq.question,
acceptedAnswer: {
'@type': 'Answer',
text: faq.answer.replace(/<[^>]*>/g, ''),
},
})),
};
注意点:
textフィールドにはHTMLタグを含めない(正規表現で除去)- Astroでは
set:html={JSON.stringify(jsonLd)}でインライン化 - Google構造化データテストツールで検証可能
壁にぶつかった瞬間と乗り越え方
「ReactなしでCTAのhover効果を作れるのか?」
ブログCTAの元となったのは、React(JSX)で書かれたコンポーネントでした。useState でhover状態を管理し、スタイルを動的に切り替える設計。
しかし、ブログには @astrojs/react を導入していない。CTAのためだけにReactランタイムを追加するのは、サイト全体の設計思想に反します。
最初は「Reactを入れるしかないか…」と思いました。
しかし、冷静にhover効果の中身を見ると、ボーダーカラーの変化とbox-shadowの追加だけ。これはCSSの :hover で完全に代替できます。
問題は、4つのバリアントごとに異なるアクセント色をどう扱うか。
突破口はCSS color-mix() 関数でした。
.cta-card:hover {
border-color: color-mix(in srgb, var(--cta-accent) 40%, transparent);
}
CSS カスタムプロパティとして色を渡し、color-mix() で透明度を動的に計算する。これで、1つのスタイルシートで4バリアントのhover効果を実現できました。
Reactの useState で実現していたことが、CSS 3行で置き換わった。 追加のJavaScriptはゼロ。バンドルサイズへの影響もゼロ。
この経験から学んだこと
「JavaScriptが必要」と思い込んでいるUIパターンの多くは、実はCSSだけで実現できる。
アコーディオン → <details>/<summary>。hover効果 → :hover + color-mix()。トグル → :checked + 隣接セレクタ。
「本当にJSが必要か?」と問い直す習慣は、パフォーマンスだけでなく、コードのシンプルさにも直結します。
この経験から得た3つの教訓
教訓1:「作って終わり」は始まりでしかない
サイトを「完成」させた時点で満足してしまいがちですが、本当の仕事はそこから始まります。
作る側の目線と使う側の目線は驚くほど違う。自分では「良い出来だ」と思っていたサイトが、クライアント目線で見ると「何をしてくれるか」「いくらかかるか」「どう進むか」が全くわからなかった。
定期的に「自分が発注する側だったら」の視点でサイトを見直す。 これだけで改善点は山ほど見つかります。
教訓2:「載せない」判断は定期的に見直す
前回の記事で「価格表は入れない」と書きました。その時点ではB2Bサイトとして正しい判断だったかもしれません。
しかし、ターゲット層が広がったり、市場環境が変わったりすれば、正解も変わります。
「あの時の判断は今でも正しいか?」を問い直す勇気が大切です。 一度決めたことに固執するのは、一貫性ではなく怠惰です。
教訓3:ブログは「集客」ではなく「信頼構築」の場
ブログにCTAを入れたのは「集客のため」ではありません。
技術記事を読んで「この人は技術力がある」と感じた読者が、スムーズにサービスページに遷移できるようにする。信頼が構築された瞬間に、行動の選択肢を提示する。
ブログとサービスサイトを「別のもの」として運営するのは、フリーランスにとって大きな機会損失です。
まとめ:今日からできるアクションプラン
この記事で解説した内容をまとめます:
- 自己診断: 自分のサービスサイトを「クライアントの目」で見直す。「発注の壁」5つの観点で課題を洗い出す
- 料金の透明性: 目安価格を掲載する。正確な見積りではなく「このくらいの規模感」が伝われば十分
- FAQの整備: 問い合わせ前の小さな疑問を解消する。
<details>/<summary>ならJS不要で実装可能 - ブログからの導線: 技術記事を読んでいる人は潜在顧客。文脈に合ったCTAで自然に誘導する
今日からできる具体的なアクション:
まずは信頼できる人(エンジニアでない人が理想)にサービスサイトを見てもらい、「問い合わせようと思うか?思わないとしたらなぜか?」を聞いてみてください。技術者の盲点は、非技術者の視点で初めて見えます。
おわりに:伝えたかったこと
最後まで読んでいただき、ありがとうございました。
この記事を書いたのは、技術的に良いサイトを作ったのに結果が出ないという経験をした過去の自分と、同じ状況にいるかもしれないエンジニアの助けになりたかったからです。
エンジニアは「作る力」には長けていますが、「使ってもらう力」は意外と苦手です。
技術力をアピールすることと、発注しやすいサイトを作ることは、似ているようでまるで違う。「良いものを作れば人は来る」は幻想で、「来た人が行動できる設計」こそが問い合わせを生む。
あなたのサービスサイトにも、きっと改善の余地があります。この記事がその第一歩になれば幸いです。
参考リンク
- 前回の記事:B2Bサービスサイトを Astro 5 + Tailwind CSS v4 で構築した設計と実装
- Astro 5 公式ドキュメント
- Schema.org FAQPage
- CSS color-mix() — MDN
- Secure Auto Lab
この記事が役に立ったら、ぜひシェアをお願いします!
あなたのシェアが、同じ悩みを持つ誰かの助けになります。
tinou
情報処理安全確保支援士とPMの資格を使ってITコンサルタントとして働く傍ら、自宅で自動化とセキュリティを研究しているエンジニア