screenshots について
screenshotsは、Playwrightでアプリのマーケティング用スクリーンショットを撮るスキルです。製品紹介やSNS、ドキュメント向けに、2倍HiDPIでの撮影とルートからの撮影候補の把握を支援します。
主な機能
- 01Playwrightでアプリ画面を撮影
- 022倍HiDPI解像度で撮影
- 03ルートを分析してページを発見
- 04ログイン後の画面撮影に対応
- 05撮影時のトラブル対応を案内
活用シーン
- 01製品リリース用画像の作成
- 02SNS向け機能画像の準備
- 03ランディングページ素材の作成
- 04ドキュメント用UI参考画像の作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ steveclarke/ dotfiles --skill screenshotsインストール後、Claude Code で/screenshotsを入力して読み込まれたか確認します。
詳細
Playwright を直接使用して、アプリのマーケティング品質のスクリーンショットを生成します。スクリーンショットは、deviceScaleFactor: 2 を使用して、真の HiDPI(2x Retina)解像度で撮影されます。
前提条件
Playwright が利用可能である必要があります。次のコマンドで確認します。
npx playwright --version 2>/dev/null || npm ls playwright 2>/dev/null | grep playwright
見つからない場合は、ユーザーに次のように伝えます。
Playwright が必要です。
npm install -D playwrightまたはnpm install -D @playwright/testでインストールしてください。
ステップ 1:アプリの URL を決定する
$1 が指定されている場合は、それをアプリの URL として使用します。
URL が指定されていない場合:
package.jsonのスクリプトを確認し、開発サーバーが起動している可能性があるか調べますAskUserQuestionを使用して、ユーザーに URL を尋ねるか、開発サーバーの起動を手伝うことを提案します
候補として提示する一般的なデフォルト URL:
http://localhost:3000(Next.js、Create React App、Rails)http://localhost:5173(Vite)http://localhost:4000(Phoenix)http://localhost:8080(Vue CLI、汎用)
ステップ 2:要件を確認する
次の質問を使用して、AskUserQuestion を実行します。
質問 1:スクリーンショットの枚数
- ヘッダー:「枚数」
- 質問:「スクリーンショットは何枚必要ですか?」
- 選択肢:
- 「3~5」 - 主要機能を手早くまとめたセット
- 「5~10」 - 機能を幅広く網羅
- 「10 以上」 - マーケティング用のフルセット
質問 2:用途
- ヘッダー:「用途」
- 質問:「これらのスクリーンショットは何に使用しますか?」
- 選択肢:
- 「Product Hunt」 - ヒーロー画像と機能のハイライト
- 「ソーシャルメディア」 - 目を引く機能デモ
- 「ランディングページ」 - マーケティング用セクションとメリット
- 「ドキュメント」 - UI リファレンスとチュートリアル
質問 3:認証
- ヘッダー:「認証」
- 質問:「スクリーンショットを撮りたい機能にアクセスするには、アプリへのログインが必要ですか?」
- 選択肢:
- 「ログイン不要」 - 公開ページのみ
- 「必要。認証情報を提供します」 - 最初にログインが必要
ユーザーが「必要。認証情報を提供します」を選択した場合は、次の追加質問をします。
- 「ログインページの URL は何ですか?」(例:
/login、/sign-in) - 「メールアドレス/ユーザー名は何ですか?」
- 「パスワードは何ですか?」
スクリプトは Playwright のスマートロケーターを使用して、ログインフォームのフィールドを自動的に検出します。
ステップ 3:機能を把握するためにコードベースを分析する
コードベースを詳しく調べてアプリを理解し、スクリーンショットの候補を特定します。
3.1:まずドキュメントを読む
アプリの機能を理解するため、必ず最初に次のファイルを読みます。
-
README.md(およびサブディレクトリ内の README ファイル) - README 全体を読み、次の点を把握します。
- アプリの概要と解決する課題
- 主な機能とできること
- すでに記載されているスクリーンショットや機能の説明
-
CHANGELOG.md または HISTORY.md - 最近追加された、紹介する価値のある機能
-
docs/ ディレクトリ - 機能に関する追加ドキュメント
3.2:ルートを分析してページを特定する
ルーティング設定を読み、利用可能なすべてのページを確認します。
| フレームワーク | 読み込むファイル | 確認する内容 |
|---|---|---|
| Next.js App Router | app/ ディレクトリの構成 | page.tsx を含む各フォルダーが 1 つのルートです |
| Next.js Pages Router | pages/ ディレクトリ | 各ファイルが 1 つのルートです |
| Rails | config/routes.rb | すべてのルートを確認するため、ファイル全体を読みます |
| React Router | createBrowserRouter または <Route を検索 | パスを含むルート定義 |
| Vue Router | src/router/index.js または router.js | パス定義を含むルート配列 |
| SvelteKit | src/routes/ ディレクトリ | +page.svelte を含む各フォルダーが 1 つのルートです |
| Remix | app/routes/ ディレクトリ | ファイルベースのルーティング |
| Laravel | routes/web.php | ルート定義 |
| Django | urls.py ファイル | URL パターン |
| Express | app.get、router.get を検索 | ルートハンドラー |
重要:ファイルが存在するかどうかを確認するだけでなく、実際にファイルを読みます。ルート定義から、スクリーンショットを撮影できるページを特定できます。
3.3:主要なコンポーネントを特定する
スクリーンショットで撮影できる機能を表すコンポーネントを探します。
- ダッシュボードのコンポーネント
- 独自の UI を持つ機能セクション
- フォームとインタラクティブな入力欄
- データの可視化(チャート、グラフ、テーブル)
- モーダルとダイアログ
- ナビゲーションとサイドバー
- 設定パネル
- ユーザープロフィールのセクション
3.4:マーケティング素材を確認する
主要な機能の手がかりとなる既存のマーケティングコンテンツを探します。
- ランディングページのコンポーネント(多くの場合、
components/landing/またはcomponents/marketing/にあります) - 機能一覧のコンポーネント
- 料金表
- お客様の声のセクション
3.5:機能一覧を作成する
見つかった機能の包括的な一覧を、以下の情報とともに作成します。
- 機能名(README またはコンポーネント名から取得)
- URL パス(ルートから取得)
- フォーカスする CSS セレクター(コンポーネントの構造から取得)
- 必要な UI の状態(ログイン済み、データ入力済み、モーダル表示中、特定のタブ選択中など)
ステップ 4:ユーザーとスクリーンショットを計画する
見つかった機能をユーザーに提示し、一覧の確認または変更を依頼します。
AskUserQuestion を使用します。
- ヘッダー:「機能」
- 質問:「コードベースで以下の機能が見つかりました。どの機能のスクリーンショットを撮影しますか?」
- 選択肢:見つかった主要な機能を3〜4個と、「撮影する機能を自分で選ぶ」を提示します
ユーザーが特定の機能を希望する場合は、撮影する内容を正確に確認するため、追加の質問をします。
ステップ 5:スクリーンショット用ディレクトリを作成する
mkdir -p screenshots
ステップ 6:Playwright スクリプトを作成して実行する
適切な HiDPI 設定で Playwright を使用する Node.js スクリプトを作成します。スクリプトでは次の処理を行います。
- 真の Retina 解像度を実現するため、
deviceScaleFactor: 2を使用する - ビューポートを 1440x900 に設定する(2880x1800 ピクセルの画像が生成されます)
- 認証情報が提供されている場合は、認証を処理する
- 各ページに移動してスクリーンショットを撮影する
スクリプトのテンプレート
このスクリプトを一時ファイル(例:screenshot-script.mjs)に書き込み、実行します。
import { chromium } from 'playwright';
const BASE_URL = '[APP_URL]';
const SCREENSHOTS_DIR = './screenshots';
// Authentication config (if needed)
const AUTH = {
needed: [true|false],
loginUrl: '[LOGIN_URL]',
email: '[EMAIL]',
password: '[PASSWORD]',
};
// Screenshots to capture
const SCREENSHOTS = [
{ name: '01-feature-name', url: '/path', waitFor: '[optional-selector]' },
{ name: '02-another-feature', url: '/another-path' },
// ... add all planned screenshots
];
async function main() {
const browser = await chromium.launch();
// Create context with HiDPI settings
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2, // This is the key for true retina screenshots
});
const page = await context.newPage();
// Handle authentication if needed
if (AUTH.needed) {
console.log('Logging in...');
await page.goto(AUTH.loginUrl);
// Smart login: try multiple common patterns for email/username field
const emailField = page.locator([
'input[type="email"]',
'input[name="email"]',
'input[id="email"]',
'input[placeholder*="email" i]',
'input[name="username"]',
'input[id="username"]',
'input[type="text"]',
].join(', ')).first();
await emailField.fill(AUTH.email);
// Smart login: try multiple common patterns for password field
const passwordField = page.locator([
'input[type="password"]',
'input[name="password"]',
'input[id="password"]',
].join(', ')).first();
await passwordField.fill(AUTH.password);
// Smart login: try multiple common patterns for submit button
const submitButton = page.locator([
'button[type="submit"]',
'input[type="submit"]',
'button:has-text("Sign in")',
'button:has-text("Log in")',
'button:has-text("Login")',
'button:has-text("Submit")',
].join(', ')).first();
await submitButton.click();
await page.waitForLoadState('networkidle');
console.log('Login complete');
}
// Capture each screenshot
for (const shot of SCREENSHOTS) {
console.log(`Capturing: ${shot.name}`);
await page.goto(`${BASE_URL}${shot.url}`);
await page.waitForLoadState('networkidle');
// Optional: wait for specific element
if (shot.waitFor) {
await page.waitForSelector(shot.waitFor);
}
// Optional: perform actions before screenshot
if (shot.actions) {
for (const action of shot.actions) {
if (action.click) await page.click(action.click);
if (action.fill) await page.fill(action.fill.selector, action.fill.value);
if (action.wait) await page.waitForTimeout(action.wait);
}
}
await page.screenshot({
path: `${SCREENSHOTS_DIR}/${shot.name}.png`,
fullPage: shot.fullPage || false,
});
console.log(` Saved: ${shot.name}.png`);
}
await browser.close();
console.log('Done!');
}
main().catch(console.error);
スクリプトの実行
node screenshot-script.mjs
実行後、一時スクリプトを削除します。
rm screenshot-script.mjs
ステップ 7:スクリーンショットの高度なオプション
要素にフォーカスしたスクリーンショット
ビューポート全体ではなく、特定の要素のスクリーンショットを撮影するには、次のようにします。
const element = await page.locator('[CSS_SELECTOR]');
await element.screenshot({ path: `${SCREENSHOTS_DIR}/element.png` });
ページ全体のスクリーンショット
スクロール可能なコンテンツは、ページ全体を撮影します。
await page.screenshot({
path: `${SCREENSHOTS_DIR}/full-page.png`,
fullPage: true
});
アニメーションの待機
ページにアニメーションがある場合は、完了するまで待機します。
await page.waitForTimeout(500); // Wait 500ms for animations
スクリーンショットの撮影前に要素をクリックする
モーダル、ドロップダウン、またはホバー状態を撮影するには、次のようにします。
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });
ダークモードのスクリーンショット
アプリがダークモードに対応している場合は、次のようにします。
// Set dark mode preference
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
colorScheme: 'dark',
});
ステップ 8:ファイル名の規則
順序を示す数字の接頭辞を付け、内容がわかる kebab-case のファイル名を使用します。
| 機能 | ファイル名 |
|---|---|
| ダッシュボードの概要 | 01-dashboard-overview.png |
| リンク管理 | 02-link-inbox.png |
| エディションエディター | 03-edition-editor.png |
| 分析 | 04-analytics.png |
| 設定 | 05-settings.png |
ステップ 9:確認して要約する
すべてのスクリーンショットを撮影したら、結果を確認します。
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
次の内容をユーザーに要約して伝えます。
- 生成されたすべてのファイルとそのパスを一覧表示する
- 解像度を確認する(ビューポートが 1440x900 の場合、2x Retina で 2880x1800 になります)
- ファイルサイズの合計を伝える
- 追加で行える操作を提案する
出力例:
Generated 5 marketing screenshots:
screenshots/
├── 01-dashboard-overview.png (1.2 MB, 2880x1800 @ 2x)
├── 02-link-inbox.png (456 KB, 2880x1800 @ 2x)
├── 03-edition-editor.png (890 KB, 2880x1800 @ 2x)
├── 04-analytics.png (567 KB, 2880x1800 @ 2x)
└── 05-settings.png (234 KB, 2880x1800 @ 2x)
All screenshots are true retina-quality (2x deviceScaleFactor) and ready for marketing use.
エラー処理
- Playwright が見つからない:
npm install -D playwrightを提案します - ページが読み込まれない:開発サーバーが実行中か確認し、起動するよう提案します
- ログインに失敗する:スマートロケーターは一般的なパターンを試しますが、一般的でないログインフォームでは失敗する場合があります。ログインに失敗した場合は、ログインページの HTML を調べて正しいセレクターを見つけ、スクリプトをカスタマイズします。
- 要素が見つからない:CSS セレクターを確認し、代わりにページ全体のスクリーンショットを撮影することを提案します
- スクリーンショットの撮影に失敗する:ディスク容量と、スクリーンショット用ディレクトリへの書き込み権限を確認します
最良の結果を得るためのヒント
- クリーンな UI 状態:現実的なコンテンツを表示するには、デモデータまたはシードデータを使用します
- サイズを統一:すべてのスクリーンショットで同じビューポートを使用します
- コンテンツの読み込みを待機:すべてのコンテンツが読み込まれるように、
waitForLoadState('networkidle')を使用します - 開発ツールを非表示:ブラウザー拡張機能や開発用オーバーレイが表示されていないことを確認します
- ダークモードのバリエーション:対応している場合は、ライトモードとダークモードの両方で撮影することを検討します