Playwright でスクショを撮らせたら、コーディングエージェントの UI 崩れ修正が10倍楽になった
E2E テストに Playwright を整備して、コーディングエージェントに変更後のスクリーンショットを撮らせるようにしたら、UI 崩れの修正サイクルが劇的に短くなった話。
この記事を共有
TL;DR
- コーディングエージェントは DOM やコードは読めるけど、実際の見た目は見ていない。だから UI 崩れに気づけない。
- Playwright で「指定ページのスクショを撮るだけ」の仕組みを整備して、エージェントに撮影 → 画像を確認 → 直す、をループさせた。
- 「直す → 撮る → 自分で見て判断する」が回り出した瞬間、UI 崩れの修正がめちゃくちゃ楽になった。体感で10倍。
- 大事なのは凝った E2E ではなく、エージェントが自分で見られるフィードバックループを渡すこと。
UI 崩れの修正がいちばんしんどい
コーディングエージェントに普通のロジックを書かせるのはだいぶ安定してきた。
テストがあれば、エージェントは赤を緑にするまで自分で回せる。
でも UI 崩れだけは別だった。
- ボタンが微妙にはみ出す
- ダークモードだけ文字が背景に溶ける
- ある画面幅でレイアウトが2段に割れる
gapが効いてなくて要素がくっつく
この手の不具合は、コードを読んでも気づけないことが多い。
そして厄介なのが、エージェントに「直して」と言っても、直ったかどうかをエージェント自身が確認できないところだ。
結局こっちがブラウザを開いて、目で見て、「まだ崩れてる」と差し戻す。
このラリーがいちばん時間を食っていた。
エージェントは画面を見ていない
冷静に考えると当たり前で、エージェントが見ているのは基本的にテキストだ。
- ソースコード
- DOM 構造
- クラス名
つまり「この div にこの Tailwind クラスが付いている」までは分かる。
でも、それが実際にどう描画されているかは見ていない。
人間がフロントを直すときは、ほぼ必ず画面を見ている。
それなのにエージェントには画面を渡していなかった。これが歪みの正体だった。
やったこと: スクショを撮る土台を渡す
立派な E2E スイートを作りたかったわけではない。
ほしかったのは「指定したページのスクショを撮って、ファイルに吐く」だけの最小の仕組み。
Playwright を入れる。
npm install -D @playwright/test
npx playwright install chromium
撮影対象を配列で並べて、ループで撮るだけのスペックを置く。
// e2e/screenshots.spec.ts
import { test, expect } from '@playwright/test';
const pages = [
{ name: 'home', path: '/' },
{ name: 'blog', path: '/blog' },
{ name: 'blog-post', path: '/blog/knip-dead-code-cleanup' },
{ name: 'projects', path: '/projects' },
];
const themes = ['light', 'dark'] as const;
for (const { name, path } of pages) {
for (const theme of themes) {
test(`screenshot ${name} (${theme})`, async ({ page }) => {
await page.emulateMedia({ colorScheme: theme });
await page.goto(path);
await page.waitForLoadState('networkidle');
await page.screenshot({
path: `e2e/__shots__/${name}-${theme}.png`,
fullPage: true,
});
});
}
}
package.json にコマンドを足しておく。
{
"scripts": {
"shots": "playwright test e2e/screenshots.spec.ts"
}
}
これで npm run shots を叩くと、各ページ・ライト/ダークのスクショが e2e/__shots__/ に並ぶ。
エージェントにループを渡す
ポイントはここからで、スクショを撮るだけでは意味がない。
撮った画像をエージェント自身に見せて、判断させるところまでをセットにする。
最近のコーディングエージェント(Claude Code など)は画像ファイルを読める。
なので、エージェントへの指示をこういう形にした。
- UI を変更する
npm run shotsを実行する- 出力された
e2e/__shots__/*.pngを自分で開いて確認する - 崩れていたら直して 2 に戻る
- 崩れがなくなったら完了
これだけで挙動がガラッと変わった。
エージェントが「ボタンが右にはみ出てますね」と自分で気づいて、max-width を足して、もう一度撮って、「直りました」と言う。
今まで人間がやっていた目視チェックのラリーが、ループの中に閉じた。
なぜ10倍楽になったのか
差し戻し回数が減ったのが大きい。
これまでは「直して」と言うたびに、こっちがブラウザを開いて確認していた。
1回の修正に人間の目視が1回必要だった。
スクショのループを渡してからは、エージェントが自分で何往復もしてから「できました」と持ってくる。
人間の確認は最後の1回でよくなった。
副次的に良かったこと。
- ダークモード崩れみたいな「気づきにくいやつ」を早い段階で拾える
- 「この画面幅で崩れる」を再現条件ごとスクショに残せる
- レビュー時に before / after の画像を貼りやすい
- 文章で「中央寄せにして」と説明するより、画像を見せたほうが早い
エージェントにとってもこっちにとっても、画像は最強の共通言語だった。
ハマったところ
万能ではないので、いくつか注意。
- 撮るタイミング。アニメーションやフォント読み込み中に撮ると毎回ブレる。
waitForLoadState('networkidle')や、必要なら明示的な待機を入れる。 - 差分は人間が見たほうがいい場面もある。エージェントは「崩れてないか」は得意だけど、「デザインとして良いか」は別の話。最後は人間が見る。
- スクショを撮りすぎない。全ページ・全幅・全テーマを撮ると遅いし、画像が多すぎて逆に見落とす。まずは崩れやすい数枚から。
- ピクセル完全一致の比較までは要らないことが多い。
toHaveScreenshot()での厳密比較は便利だけど、最初は「撮って目で見る」だけで十分効く。
まとめ
UI 崩れの修正がしんどかった原因は、エージェントが画面を見ていなかったこと。
立派な E2E を組む必要はなくて、「スクショを撮って、エージェント自身に見せる」最小ループを渡すだけでよかった。
それだけで、修正の往復がエージェントの中で完結して、体感で10倍楽になった。
フロントをエージェントに任せていて崩れに困っているなら、まず Playwright で1枚撮らせてみるところから試す価値がある。