web-visual-assets について
web-visual-assetsは、教育サイト向けのビジュアル素材を作成・統合するスキルです。スクリーンショット、生成画像、SVG図、QRコードに対応し、フォールバックと網羅性の確認方法も扱います。
主な機能
- 01Playwrightによるスクリーンショット
- 02AI生成イラスト
- 03手描きSVG図
- 04QRコード生成
- 05SPAへの素材統合
活用シーン
- 01コースページに製品画面を追加する
- 02コースの概念図を一貫した画風で作る
- 03ラベル付きのフロー図を作成する
- 04コースリンクのQRコードを生成する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ kevintsai1202/ teaching-site-skills --skill web-visual-assetsインストール後、Claude Code で/web-visual-assetsを入力して読み込まれたか確認します。
詳細
Web ビジュアルアセット
スキーマの定義元:
Illustrationプリミティブの形状({name, kind, alt, spec}および{kind: 'waived', reason})と、ユニットごとのillustrations[]カバレッジ下限は、_shared/domain-primitives.md§11 で定義されています。
このスキルでは、教材サイトのビジュアルレイヤーを作成します。4つのアセットソースでほぼすべてのニーズに対応できますが、それぞれトレードオフがあります。
アセットソースの選択フローチャート
What do you need?
├── Screenshot of a real product/website → Source 1: Playwright scraping
├── A conceptual scene (workflow, metaphor) → Source 2: AI image generation
├── A simple diagram (boxes, arrows, labels) → Source 3: Hand-drawn SVG
└── A scannable code / functional artifact → Source 4: Code generator (QRCode, etc.)
ソース1:Playwright によるスクレイピング
ツール、Webサイト、プロフィールカードのスクリーンショットに使用します。サンプルのワークショップでは、AIツールのホームページ6件とYouTube講師チャンネルをスクレイピングします。
標準的なヘッドレスフロー
// scripts/scrape-tools.mjs
import { chromium } from 'playwright';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await page.goto(url, { waitUntil: 'networkidle' });
await page.screenshot({ path: `data/tools/${id}.png`, fullPage: false });
await browser.close();
ボット対策にブロックされた場合(CDPモード)
一部のサイト(Cloudflare、最近のOpenAIのサイトなど)はヘッドレスChromiumを検出してブロックします。回避策として、CDP経由で実際のChromeに接続します。
# scripts/start-cdp-chrome.ps1
Start-Process chrome.exe -ArgumentList '--remote-debugging-port=9222', '--user-data-dir=C:\tmp\cdp-profile'
const browser = await chromium.connectOverCDP('http://localhost:9222');
const page = (await browser.contexts()[0].pages())[0];
// ... drive an existing real-browser session
--cdp --pause フラグを付けて実行し、人間がCAPTCHAを完了できるようにしてから、Enterキーを押して続行します。
サブセットのマージパターン(他の項目を上書きしない)
複数項目のうち1つだけを再スクレイピングする場合、既存データを上書きするのではなくマージします。
async function writeMerged(jsonPath, updates) {
const existing = JSON.parse(await fs.readFile(jsonPath, 'utf8').catch(() => '{}'));
await fs.writeFile(jsonPath, JSON.stringify({ ...existing, ...updates }, null, 2));
}
CLI:node scrape-tools.mjs --ids codex,notebooklm は、その2つだけを処理します。
YouTubeチャンネル特有の注意点
- チャンネルページはSPAです。
?sort=p&view=0のようなURLクエリ文字列は除去されます。最新のN本の動画を取得してから、クライアント側で並べ替えてください。 - 仮想スクロールでは、表示範囲外の
<img>からsrcが除去されます。videoIdからサムネイルURLを導出してください:https://img.youtube.com/vi/{videoId}/hqdefault.jpg。DOMのsrcに依存しないでください。
ソース2:AI画像生成
シナリオのイラスト、各日のヒーロー画像、概念的なシーンに使用します。サンプルのワークショップではGeminiの画像生成を使用していますが、どのテキスト画像生成APIでも利用できます。
プロンプトの作成ルール
- コース全体で一貫したビジュアルスタイルを指定します(例:「フラットなイラスト、柔らかいパステルカラー、テキストなし、人物なし」)。イラストに一貫性がないと、素人っぽく見えます。
- 顔やブランドロゴは避けます。AI画像生成ツールはどちらも苦手で、不自然な結果になることがあります。
- 画像と一緒にプロンプトを保存します。
assets/illustrations/
├── day1-token-prediction.png
├── day1-token-prediction.prompt.md ← regenerate-ready prompt
└── ...
こうしておけば、後から高品質な画像を再生成したり、スタイルを調整したりできます。
PNG + SVG のフォールバック描画
AI画像生成は、ときどき失敗します(レート制限、コンテンツフィルター、モデルの問題など)。AI生成のPNGごとに、同じパスのベース名で手描きのSVGフォールバックを用意します。
function renderIllustration(name) {
const img = el('img', { src: `assets/illustrations/${name}.png`, alt: '' });
img.onerror = () => { img.src = `assets/illustrations/${name}.svg`; img.onerror = null; };
return img;
}
ブラウザーはまずPNGを試し、404の場合はSVGに切り替えます。フォールバックは、使わない場合でも同梱します。リンク切れへの備えになります。
ソース3:手描きのSVG
名前付きボックス間の矢印、手順ごとのフロー、ラベル付きのUIモックアップ、教室の地図など、正確な制御が求められる図に使用します。
手書きのSVG(またはFigma/ExcalidrawからエクスポートしたSVG)は、次のような用途ではAIより適しています。
- テキストラベル(AIは画像内の日本語テキストを正確に描画できません)
- 矢印とボックスのフロー(AIはレイアウトを間違えることがあります)
- コースの詳細が変わったときに更新が必要なもの
スタイルのヒント:SVG自体が線画でも、半透明のコンテナーと drop-shadow フィルターで囲むと、サイト全体の美的スタイルに合わせられます。
ソース4:生成コード(QR)
import QRCode from 'qrcode';
await QRCode.toFile('assets/qr/workshop-url.png', 'https://your-workshop.example/', {
width: 512,
margin: 1,
color: { dark: '#000000', light: '#FFFFFF' }
});
ダークテーマのサイトでは、白地に黒のコードを生成します。黒地に白のコードではありません。ほとんどのQRスキャナーは、明るい背景に暗い前景色のコードを必要とします。
アセットフォルダーの規則
assets/
├── tools/ ← Source 1: scraped tool/product screenshots
├── illustrations/ ← Source 2 (PNG) + Source 3 (SVG fallback)
├── scenarios/ ← Source 2: per-unit scene illustrations
├── cases/ ← Source 2: shared case visuals
├── characters/ ← Source 2: persona portraits
├── qr/ ← Source 4: QR codes
└── maps/ ← Source 1 (screenshot) or Source 3 (SVG)
SPAへのアセットの組み込み
course-data.js では、各ユニットに illustrations[] 配列があり(1~3件)、Stage 2で記述した 圖片需求 ブロックから内容を設定します。
{
id: 'u-3',
title: '...',
illustrations: [
{ name: 'day1-u3-hero.png', kind: 'hero', alt: '...', spec: '...' },
{ name: 'day1-u3-flow.svg', kind: 'diagram', alt: '...', spec: '...' },
{ name: 'day1-u3-example.png', kind: 'screenshot', alt: '...', spec: '...' } // optional 3rd
],
// ...
}
renderUnit は unit.illustrations を反復処理して renderIllustration(entry) を呼び出し、各項目のPNG優先/SVGフォールバックを処理します。最初にヒーロー画像(ファーストビューより上)、次に図、最後にスクリーンショットの順で描画します。
旧式の単一illustrationフィールド:古い course-data.js ファイルでは、illustration: 'foo.png'(単一の文字列)が使われています。これを illustrations: [{ name: 'foo.png', kind: 'hero' }] として扱い、都合のよいときに配列形式へ移行してください。新しいサイトでは旧形式に依存しないでください。
カバレッジの最低基準(厳守 — 1ユニットあたり1~3点の図版)
カバー画像しかない教材サイトは、未完成の草稿のように見えます。サイトを機能完成と宣言する前に、ステージ5で最低カバレッジを満たす必要があります。
-
course-data.jsのすべてのユニットで、illustrations.length >= 1(かつ<= 3)であること。 - 各ユニットに、
assets/内の実ファイル(PNG または SVG)が少なくとも1点あること。すべてが{ kind: 'placeholder' }のユニットは不合格です。 - 画像を使わない正当な理由があるユニットには、明示的な免除を記載すること:
illustrations: [{ kind: 'waived', reason: '...' }]。何も記載しないこと、つまり空の配列は最低基準を満たしません。 - ユニットごとの画像とは別に、カバー画像(
assets/cover.pngまたは同等のもの)があること。カバー画像は、各ユニットの1~3点の上限・下限には含まれません。
最低基準を満たしていない場合、サイトを「機能完成」と宣言できません。また、ステージ5b/6をまだ実行してはいけません。オーケストレーター層での適用については、teaching-site/SKILL.md の「ステージ5 画像カバレッジの最低基準」を参照してください。
バッチ生成パターン(推奨)
画像を1ユニットずつ生成しないでください。course-data.js からマニフェストを作成し、バッチで実行します。
// scripts/generate-illustrations.mjs
const manifest = [];
for (const dayKey of ['day1', 'day2', 'day3', 'day4']) {
for (const unit of window.COURSE[dayKey].units) {
for (const ill of (unit.illustrations || [])) {
if (ill.kind === 'waived') continue;
manifest.push({ name: ill.name, prompt: ill.spec, kind: ill.kind });
}
}
}
// → feed manifest to AI image API in parallel batches of 4–6
バッチ生成する理由:スタイルの一貫性を保つためです。単一ユニットごとに場当たり的に生成すると、スタイルにばらつきが生じます。同じスタイルの接頭辞を使ってバッチ生成すれば、統一感のあるビジュアルシステムを構築できます。
検証
検証には、それぞれ専用のスキルがある2つの観点があります。
- 「参照されているアセットはディスク上に存在するか?」(ファイル間の参照チェック)→
web-content-auditを実行します。Audit 1(Cross-Artifact Reference Resolution)とアセットカバレッジ監査で、これを正確に確認できます。 - 「アセットはブラウザーで404エラーなく実際に表示されるか?」(実行時チェック)→
web-visual-verificationを実行します。console.errorを収集する複数ビューポート対応の検証スクリプトを使えば、壊れた<img>リクエストを自動的に検出できます。
デプロイの前に、両方を実行してください。監査では存在しないファイルへの参照を検出し、検証ではファイルは存在するものの、レンダリングされたHTMLでパスが間違っているアセットを検出します。失敗する原因が異なるため、それぞれ別のスクリプトが必要です。
アンチパターン
- スタイルの一貫性を保たずに画像を1点ずつ生成する — スタイル仕様を一度作成し、すべてのプロンプトの接頭辞として貼り付けます。
- 画像にAI生成テキストを含める — ほとんどの場合、正しく表示されません。正確なテキストが必要な画像にはSVGを使います。
- AI生成PNGのSVGフォールバックがない — PNGへのリンクが切れる(ファイルの削除や名前の変更ミスなど)と、サイトに画像切れアイコンが表示されます。SVGフォールバックがあれば、問題を最小限に抑えられます。
- 再スクレイピング用スクリプトを用意せずにスクリーンショットを保存する — 6か月後に誰かが最新のスクリーンショットを必要としても、元のスクリーンショットをどう取得したか記録が残っていません。
引き継ぎ
ユーザーに次のように伝えます:「ビジュアルアセットを配置しました。node scripts/verify-assets.mjs を実行して不足がないことを確認してください。サイトはこれで機能完成です。次のステージ(course-ebook-publishing)では、同じコンテンツをPDF/DOCX形式の成果物にできます。ウェブ版が安定してからのみ実行してください。」